<?php
declare(strict_types=1);
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/_debug.log');

require_once __DIR__ . '/common/web_session_auth.php';

golder_web_app_session_start();
header('Content-Type: text/html; charset=utf-8');

$DEBUG_MODE = (isset($_GET['debug']) && (string)$_GET['debug'] === '1');
if ($DEBUG_MODE) {
    ini_set('display_errors', '1');
    ini_set('display_startup_errors', '1');
    error_reporting(E_ALL);
}

$MASTER_DB = [
    'host' => '127.0.0.1',
    'port' => 3306,
    'name' => 'golder_master',
    'user' => 'goldermaster',
    'pass' => 'Mmty2676*',
];
$SUPPORTED_CURRENCIES = ['TRY', 'USD', 'EUR', 'GBP'];
$SUPPORTED_STOCK_TYPES = ['Product', 'Ziynet', 'Scrap'];
$REMEMBER_ME_COOKIE = 'golder_rm';
$REMEMBER_ME_DAYS = 365;
$WEB_HARDWARE_COOKIE = 'golder_web_hwid';
$WEB_HARDWARE_COOKIE_DAYS = 3650;
$WEB_DEVICE_NAME_COOKIE = 'golder_web_dev_name';
$WEB_DEVICE_NAME_COOKIE_DAYS = 3650;
$AVAILABLE_PERMISSION_OPTIONS = [
    'perm_product_purchase' => 'Altin Alis',
    'perm_product_sale' => 'Altin Satis',
    'perm_scrap_purchase' => 'Hurda Alis',
    'perm_scrap_sale' => 'Hurda Satis',
    'perm_ziynet_purchase' => 'Ziynet Alis',
    'perm_ziynet_sale' => 'Ziynet Satis',

    'perm_shopping' => 'Alisveris Modul Yetkisi (Eski)',

    'perm_add_income' => 'Gelir Ekle',
    'perm_add_expense' => 'Gider Ekle',
    'perm_transfer' => 'Hesaplar Arasi Transfer',
    'perm_scheduled' => 'Vadeli Islemler',
    'perm_cash_view' => 'Kasa Goruntuleme',
    'perm_card_view' => 'Kart Goruntuleme',
    'perm_exchange' => 'Doviz Islemleri',

    'perm_stock_product' => 'Altin Stok',
    'perm_def_product' => 'Altin Tanimlama',
    'perm_stock_ziynet' => 'Ziynet Stok',
    'perm_def_ziynet' => 'Ziynet Tanimlama',
    'perm_stock_scrap' => 'Hurda Stok',
    'perm_def_scrap' => 'Hurda Tanimlama',
    'perm_depots' => 'Depolar',

    'perm_accounts' => 'Cari Hesaplar',
    'perm_contacts' => 'Musteri Yonetimi',
    'perm_repair' => 'Tamir Takip',
    'perm_services' => 'Hizmetler',

    'perm_reports' => 'Raporlama',
    'perm_agenda' => 'Ajanda',
    'perm_search' => 'Arama',
    'perm_calculator' => 'Hesap Makinesi',
    'perm_zakat' => 'Zekat Hesaplama (Magaza)',
    'perm_zakat_guest' => 'Zekat Hesaplama (Musteri)',
];

require_once __DIR__ . '/common/tax_service.php';
require_once __DIR__ . '/common/zakat_service.php';

function e($v): string
{
    return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
}
$WEB_LANG_DATA = null;
$WEB_LANG_CODE = null;
function web_lang_available_codes(): array
{
    static $codes = null;
    if (is_array($codes)) {
        return $codes;
    }
    $codes = [];
    foreach (glob(__DIR__ . '/lang/*.json') ?: [] as $file) {
        $codes[] = strtoupper((string)pathinfo($file, PATHINFO_FILENAME));
    }
    $codes = array_values(array_unique(array_filter($codes)));
    sort($codes);
    if (!$codes) {
        $codes = ['TR'];
    }
    return $codes;
}
function web_lang_normalize_code(string $code): string
{
    $code = strtoupper(trim($code));
    return in_array($code, web_lang_available_codes(), true) ? $code : 'TR';
}
function web_lang_code(): string
{
    global $WEB_LANG_CODE, $tenant;
    if (is_string($WEB_LANG_CODE) && $WEB_LANG_CODE !== '') {
        return $WEB_LANG_CODE;
    }
    $langCode = '';
    if (($tenant ?? null) instanceof PDO && !empty($_SESSION['profile']['id'])) {
        try {
            $st = $tenant->prepare("SELECT setting_value FROM app_settings WHERE profile_id=:pid AND setting_key='language' LIMIT 1");
            $st->execute([':pid' => (string)$_SESSION['profile']['id']]);
            $langCode = trim((string)$st->fetchColumn());
        } catch (Throwable $_ignored) {
        }
    }
    if ($langCode === '') {
        $langCode = trim((string)($_COOKIE['golder_lang'] ?? ''));
    }
    if ($langCode === '') {
        $acceptLang = strtolower(trim((string)($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')));
        if (str_starts_with($acceptLang, 'en')) {
            $langCode = 'EN';
        } else {
            $langCode = 'TR';
        }
    }
    $WEB_LANG_CODE = web_lang_normalize_code($langCode);
    return $WEB_LANG_CODE;
}
function lang(string $key, string $default = ''): string
{
    global $WEB_LANG_DATA;
    if (!is_array($WEB_LANG_DATA)) {
        $file = __DIR__ . '/lang/' . web_lang_code() . '.json';
        $WEB_LANG_DATA = [];
        if (is_file($file)) {
            $decoded = json_decode((string)file_get_contents($file), true);
            if (is_array($decoded)) {
                $WEB_LANG_DATA = $decoded;
            }
        }
        if (!$WEB_LANG_DATA && web_lang_code() !== 'TR') {
            $fallback = __DIR__ . '/lang/TR.json';
            if (is_file($fallback)) {
                $decoded = json_decode((string)file_get_contents($fallback), true);
                if (is_array($decoded)) {
                    $WEB_LANG_DATA = $decoded;
                }
            }
        }
    }
    return (string)($WEB_LANG_DATA[$key] ?? ($default === '' ? $key : $default));
}
function ui_text(string $key, string $tr, string $en = ''): string
{
    $fallback = web_lang_code() === 'EN' ? ($en !== '' ? $en : $tr) : $tr;
    return lang($key, $fallback);
}
function role_label(string $role): string
{
    return strtolower(trim($role)) === 'admin'
        ? ui_text('role_admin', 'Yönetici', 'Admin')
        : ui_text('role_employee', 'Çalışan', 'Staff');
}
function uuid4(): string
{
    $b = random_bytes(16);
    $b[6] = chr((ord($b[6]) & 0x0f) | 0x40);
    $b[8] = chr((ord($b[8]) & 0x3f) | 0x80);
    $h = bin2hex($b);
    return substr($h, 0, 8) . '-' . substr($h, 8, 4) . '-' . substr($h, 12, 4) . '-' . substr($h, 16, 4) . '-' . substr($h, 20, 12);
}
function pdoConnect(string $host, int $port, string $db, string $user, string $pass): PDO
{
    return new PDO(
        "mysql:host={$host};port={$port};dbname={$db};charset=utf8mb4",
        $user,
        $pass,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false,
        ]
    );
}
function baseUrl(): string
{
    return strtok($_SERVER['REQUEST_URI'] ?? ($_SERVER['PHP_SELF'] ?? 'index.php'), '?') ?: 'index.php';
}
function toUrl(array $query = []): string
{
    return $query ? baseUrl() . '?' . http_build_query($query) : baseUrl();
}
function go(array $query = []): void
{
    header('Location: ' . toUrl($query));
    exit;
}
function flash(string $type, string $msg): void
{
    $_SESSION['_flash'] = ['type' => $type, 'msg' => $msg];
}
function pullFlash(): ?array
{
    $f = $_SESSION['_flash'] ?? null;
    unset($_SESSION['_flash']);
    return is_array($f) ? $f : null;
}
function clearSessionAll(): void
{
    $_SESSION = [];
    if (ini_get('session.use_cookies')) {
        $p = session_get_cookie_params();
        setcookie(session_name(), '', time() - 42000, $p['path'] ?? '/', $p['domain'] ?? '', $p['secure'] ?? false, $p['httponly'] ?? true);
    }
    session_destroy();
}
function rememberCookieSecure(): bool
{
    return !empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off';
}
function rememberCookieOptions(int $expiresTs): array
{
    return [
        'expires' => $expiresTs,
        'path' => '/',
        'secure' => rememberCookieSecure(),
        'httponly' => true,
        'samesite' => 'Lax',
    ];
}
function rememberTokenHash(string $validator): string
{
    return hash('sha256', $validator);
}
function rememberParseCookieValue(string $cookieValue): ?array
{
    $parts = explode(':', trim($cookieValue), 2);
    if (count($parts) !== 2) {
        return null;
    }
    $selector = trim($parts[0]);
    $validator = trim($parts[1]);
    if ($selector === '' || $validator === '') {
        return null;
    }
    if (!preg_match('/^[a-f0-9]{16,64}$/i', $selector)) {
        return null;
    }
    if (!preg_match('/^[a-f0-9]{32,256}$/i', $validator)) {
        return null;
    }
    return [$selector, $validator];
}
function rememberCookiePairFromRequest(string $cookieName): ?array
{
    $raw = (string)($_COOKIE[$cookieName] ?? '');
    if ($raw === '') {
        return null;
    }
    return rememberParseCookieValue($raw);
}
function rememberClearCookie(string $cookieName): void
{
    $opts = rememberCookieOptions(time() - 86400);
    setcookie($cookieName, '', $opts);
    unset($_COOKIE[$cookieName]);
}
function ensureCustomerRememberTokensTable(PDO $master): void
{
    static $done = [];
    $obj = spl_object_id($master);
    if (!empty($done[$obj])) {
        return;
    }
    $master->exec(
        "CREATE TABLE IF NOT EXISTS customer_remember_tokens (
            id BIGINT AUTO_INCREMENT PRIMARY KEY,
            customer_id BIGINT NOT NULL,
            selector CHAR(16) NOT NULL,
            validator_hash CHAR(64) NOT NULL,
            user_agent_hash CHAR(64) DEFAULT NULL,
            expire_date DATETIME NOT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            UNIQUE KEY uniq_selector (selector),
            KEY idx_customer_exp (customer_id, expire_date)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
    );
    $done[$obj] = true;
}
function rememberDeleteTokenBySelector(PDO $master, string $selector): void
{
    $selector = trim($selector);
    if ($selector === '') {
        return;
    }
    ensureCustomerRememberTokensTable($master);
    $master->prepare("DELETE FROM customer_remember_tokens WHERE selector = :s")->execute([':s' => $selector]);
}
function rememberDeleteTokenFromCookie(PDO $master, string $cookieName): void
{
    $pair = rememberCookiePairFromRequest($cookieName);
    if ($pair) {
        rememberDeleteTokenBySelector($master, (string)$pair[0]);
    }
}
function rememberIssueToken(PDO $master, string $customerId, string $cookieName, int $days = 30): void
{
    $customerId = trim($customerId);
    if ($customerId === '') {
        return;
    }
    ensureCustomerRememberTokensTable($master);
    $selector = bin2hex(random_bytes(8));
    $validator = bin2hex(random_bytes(32));
    $expireTs = time() + max(1, $days) * 86400;
    $expireSql = date('Y-m-d H:i:s', $expireTs);
    $uaHash = hash('sha256', strtolower(trim((string)($_SERVER['HTTP_USER_AGENT'] ?? 'unknown'))));
    $master->prepare(
        "INSERT INTO customer_remember_tokens
        (customer_id, selector, validator_hash, user_agent_hash, expire_date, created_at)
        VALUES (:cid, :sel, :vh, :uah, :exp, NOW())"
    )->execute([
        ':cid' => $customerId,
        ':sel' => $selector,
        ':vh' => rememberTokenHash($validator),
        ':uah' => $uaHash,
        ':exp' => $expireSql,
    ]);
    setcookie($cookieName, $selector . ':' . $validator, rememberCookieOptions($expireTs));
}
function rememberRotateToken(PDO $master, string $customerId, string $cookieName, int $days = 30): void
{
    rememberDeleteTokenFromCookie($master, $cookieName);
    rememberIssueToken($master, $customerId, $cookieName, $days);
}

function customerSessionPayload(array $customer): array
{
    $effective = golder_customer_effective_plan($customer);

    return [
        'id' => (string)($customer['id'] ?? ''),
        'username' => (string)($customer['username'] ?? ''),
        'email' => (string)($customer['email'] ?? ''),
        'phone' => (string)($customer['phone_number'] ?? ''),
        'phone_number' => (string)($customer['phone_number'] ?? ''),
        'plan_type' => (string)($effective['plan_type'] ?? ''),
        'desktop_expiry_date' => (string)($effective['desktop_expiry_date'] ?? ''),
        'sync_expiry_date' => (string)($effective['sync_expiry_date'] ?? ''),
        'sync_status' => (string)($effective['sync_status'] ?? 'disabled'),
        'web_mobile_enabled' => !empty($effective['web_mobile_enabled']) ? 1 : 0,
        'expiry_date' => (string)($effective['expiry_date'] ?? ''),
        'license_status' => (string)($customer['license_status'] ?? 'active'),
    ];
}

function customerTenantPayload(array $customer): array
{
    return [
        'db_name' => (string)($customer['mysql_db_name'] ?? ''),
        'db_user' => (string)($customer['mysql_db_user'] ?? ''),
        'db_pass' => (string)($customer['mysql_db_pass'] ?? ''),
    ];
}

function rememberTryAutoCustomerLogin(array $masterCfg, string $cookieName): ?array
{
    $pair = rememberCookiePairFromRequest($cookieName);
    if (!$pair) {
        return null;
    }
    [$selector, $validator] = $pair;
    try {
        $master = pdoConnect(
            (string)$masterCfg['host'],
            (int)$masterCfg['port'],
            (string)$masterCfg['name'],
            (string)$masterCfg['user'],
            (string)$masterCfg['pass']
        );
        ensureCustomerRememberTokensTable($master);
        $st = $master->prepare(
            "SELECT t.customer_id, t.validator_hash, t.user_agent_hash, t.expire_date,
                    c.id, c.username, c.email, c.phone_number, c.plan_type, c.expiry_date,
                    c.desktop_expiry_date, c.sync_expiry_date, c.license_status,
                    c.max_devices, c.max_pc_devices, c.max_web_sessions,
                    c.mysql_db_name, c.mysql_db_user, c.mysql_db_pass
             FROM customer_remember_tokens t
             INNER JOIN customers c ON c.id = t.customer_id
             WHERE t.selector = :sel
             LIMIT 1"
        );
        $st->execute([':sel' => $selector]);
        $row = $st->fetch();
        if (!$row) {
            rememberClearCookie($cookieName);
            return null;
        }
        $expireAt = strtotime((string)($row['expire_date'] ?? ''));
        if ($expireAt === false || $expireAt < time()) {
            rememberDeleteTokenBySelector($master, $selector);
            rememberClearCookie($cookieName);
            return null;
        }
        $uaHash = hash('sha256', strtolower(trim((string)($_SERVER['HTTP_USER_AGENT'] ?? 'unknown'))));
        $rowUa = trim((string)($row['user_agent_hash'] ?? ''));
        if ($rowUa !== '' && !hash_equals($rowUa, $uaHash)) {
            rememberDeleteTokenBySelector($master, $selector);
            rememberClearCookie($cookieName);
            return null;
        }
        $storedHash = trim((string)($row['validator_hash'] ?? ''));
        if ($storedHash === '' || !hash_equals($storedHash, rememberTokenHash($validator))) {
            rememberDeleteTokenBySelector($master, $selector);
            rememberClearCookie($cookieName);
            return null;
        }
        $customer = golder_enforce_customer_plan($master, (int)($row['id'] ?? 0));
        if (is_array($customer)) {
            $row = array_merge($row, $customer);
        }
        $master->prepare("UPDATE customers SET last_pc_login_date = NOW() WHERE id = :id")->execute([':id' => $row['id']]);
        return $row ?: null;
    } catch (Throwable $_e) {
        return null;
    }
}
function parseAmount(string $raw): float
{
    $s = str_replace([' ', "\xc2\xa0"], '', trim($raw));
    $s = str_replace(',', '.', $s);
    return (is_numeric($s) ? (float)$s : 0.0);
}
function normDate(?string $raw): string
{
    $t = strtotime(trim((string)$raw));
    return $t === false ? date('Y-m-d') : date('Y-m-d', $t);
}
function trDate(?string $raw): string
{
    $raw = trim((string)$raw);
    if ($raw === '') {
        return '';
    }
    $t = strtotime($raw);
    return $t === false ? $raw : date('d.m.Y', $t);
}
function dashboardNowLabel(): string
{
    $days = ['Pazar', 'Pazartesi', 'Sali', 'Carsamba', 'Persembe', 'Cuma', 'Cumartesi'];
    $months = [
        1 => 'Ocak',
        2 => 'Subat',
        3 => 'Mart',
        4 => 'Nisan',
        5 => 'Mayis',
        6 => 'Haziran',
        7 => 'Temmuz',
        8 => 'Agustos',
        9 => 'Eylul',
        10 => 'Ekim',
        11 => 'Kasim',
        12 => 'Aralik',
    ];
    $dayName = $days[(int)date('w')] ?? '';
    $monthName = $months[(int)date('n')] ?? date('m');
    return date('d') . ' ' . $monthName . ' ' . date('Y') . ' ' . $dayName . ' - ' . date('H:i:s');
}
function normalizeCatSub(string $cat, string $sub): array
{
    $cat = trim($cat);
    $sub = trim($sub);
    $catMap = [
        'money' => 'finance',
        'stock' => 'stocks',
        'customer' => 'customers',
    ];
    $subMap = [
        'money-cashflow' => 'finance-cashflow',
        'money-transfer' => 'finance-transfer',
        'money-accounts' => 'finance-accounts',

        'stock-definitions' => 'stock-def-product',
        'stock-depots' => 'stock-depots',
        'stock-barcode' => 'stock-product',

        'customer-persons' => 'customer-persons',
        'customer-accounts' => 'customer-accounts',
    ];
    if (isset($catMap[$cat])) {
        $cat = $catMap[$cat];
    }
    if (isset($subMap[$sub])) {
        $sub = $subMap[$sub];
    }
    return [$cat, $sub];
}
function routeFromReq(): array
{
    $q = [];
    foreach (['cat', 'sub', 'person', 'op'] as $k) {
        $v = trim((string)($_REQUEST["return_{$k}"] ?? $_GET[$k] ?? ''));
        if ($v !== '') {
            $q[$k] = $v;
        }
    }
    [$normCat, $normSub] = normalizeCatSub((string)($q['cat'] ?? ''), (string)($q['sub'] ?? ''));
    if ($normCat !== '') {
        $q['cat'] = $normCat;
    }
    if ($normSub !== '') {
        $q['sub'] = $normSub;
    }
    return $q;
}
function normalizeStockType(string $raw): string
{
    $v = strtolower(trim($raw));
    if (in_array($v, ['product', 'altin', 'gold'], true)) {
        return 'Product';
    }
    if (in_array($v, ['ziynet', 'jewelry'], true)) {
        return 'Ziynet';
    }
    if (in_array($v, ['scrap', 'hurda'], true)) {
        return 'Scrap';
    }
    return 'Product';
}
function stockTypeLabel(string $type): string
{
    $t = normalizeStockType($type);
    if ($t === 'Product') {
        return 'Altin';
    }
    if ($t === 'Ziynet') {
        return 'Altin Ziynet';
    }
    return 'Hurda';
}
function stockDefinitionPermissionForType(string $stockType): string
{
    $t = normalizeStockType($stockType);
    if ($t === 'Ziynet') {
        return 'perm_def_ziynet';
    }
    if ($t === 'Scrap') {
        return 'perm_def_scrap';
    }
    return 'perm_def_product';
}
function isCurrencyType(string $type, array $supportedCurrencies): bool
{
    $t = strtoupper(trim($type));
    return $t !== '' && in_array($t, $supportedCurrencies, true);
}
function formatAmountByType(float $amount, string $type, array $supportedCurrencies): string
{
    $dec = isCurrencyType($type, $supportedCurrencies) ? 2 : 3;
    return number_format($amount, $dec, ',', '.');
}
function sortDashboardBalances(array $balances, array $supportedCurrencies): array
{
    $order = [];
    foreach ($supportedCurrencies as $idx => $currency) {
        $order[strtoupper(trim((string)$currency))] = $idx;
    }
    uksort($balances, static function ($left, $right) use ($order): int {
        $leftKey = strtoupper(trim((string)$left));
        $rightKey = strtoupper(trim((string)$right));
        $leftKnown = array_key_exists($leftKey, $order);
        $rightKnown = array_key_exists($rightKey, $order);
        if ($leftKnown && $rightKnown) {
            return $order[$leftKey] <=> $order[$rightKey];
        }
        if ($leftKnown) {
            return -1;
        }
        if ($rightKnown) {
            return 1;
        }
        return strcasecmp($leftKey, $rightKey);
    });
    return $balances;
}
function splitDynamicLabels(string $raw): array
{
    $labels = [];
    foreach (preg_split('/[\r\n,;]+/', $raw) ?: [] as $part) {
        $label = trim((string)$part);
        if ($label === '') {
            continue;
        }
        $labels[$label] = $label;
    }
    return array_values($labels);
}
function decodeJsonList($raw): array
{
    if (is_array($raw)) {
        return array_values(array_filter(array_map(static fn($v) => trim((string)$v), $raw), static fn($v) => $v !== ''));
    }
    $raw = trim((string)$raw);
    if ($raw === '') {
        return [];
    }
    $decoded = json_decode($raw, true);
    if (!is_array($decoded)) {
        return [];
    }
    return array_values(array_filter(array_map(static fn($v) => trim((string)$v), $decoded), static fn($v) => $v !== ''));
}
function serviceDefinitionCustomFields(array $row): array
{
    return decodeJsonList($row['custom_columns_json'] ?? '');
}
function serviceDefinitionCloneFields(array $row): array
{
    return decodeJsonList($row['clone_columns_json'] ?? '');
}
function serviceDecodeDynamicPayload($raw): array
{
    if (is_array($raw)) {
        $decoded = $raw;
    } else {
        $raw = trim((string)$raw);
        $decoded = ($raw !== '' ? json_decode($raw, true) : []);
    }
    if (!is_array($decoded)) {
        $decoded = [];
    }
    $custom = [];
    $clones = [];
    $payments = [];
    if (isset($decoded['custom']) && is_array($decoded['custom'])) {
        foreach ($decoded['custom'] as $label => $value) {
            $label = trim((string)$label);
            if ($label === '') {
                continue;
            }
            $custom[$label] = trim((string)$value);
        }
    }
    if (isset($decoded['clones']) && is_array($decoded['clones'])) {
        foreach ($decoded['clones'] as $label => $values) {
            $label = trim((string)$label);
            if ($label === '') {
                continue;
            }
            $cloneValues = [];
            if (is_array($values)) {
                foreach ($values as $value) {
                    $value = trim((string)$value);
                    if ($value !== '') {
                        $cloneValues[] = $value;
                    }
                }
            } else {
                foreach (preg_split('/[\r\n]+/', (string)$values) ?: [] as $value) {
                    $value = trim((string)$value);
                    if ($value !== '') {
                        $cloneValues[] = $value;
                    }
                }
            }
            if ($cloneValues) {
                $clones[$label] = $cloneValues;
            }
        }
    }
    if (isset($decoded['payments']) && is_array($decoded['payments'])) {
        foreach ($decoded['payments'] as $payment) {
            if (!is_array($payment)) {
                continue;
            }
            $amount = (float)($payment['amount'] ?? 0);
            if ($amount <= 0) {
                continue;
            }
            $payments[] = [
                'amount' => $amount,
                'currency' => strtoupper(trim((string)($payment['currency'] ?? 'TRY'))),
                'date' => trim((string)($payment['date'] ?? '')),
                'note' => trim((string)($payment['note'] ?? '')),
            ];
        }
    }
    if (!$custom && !$clones && !$payments) {
        foreach ($decoded as $label => $value) {
            if (in_array((string)$label, ['custom', 'clones', 'payments'], true)) {
                continue;
            }
            if (is_scalar($value) && trim((string)$value) !== '') {
                $custom[trim((string)$label)] = trim((string)$value);
            }
        }
    }
    return [
        'custom' => $custom,
        'clones' => $clones,
        'payments' => $payments,
    ];
}
function serviceBuildDynamicPayloadFromRequest(): array
{
    $payload = [
        'custom' => [],
        'clones' => [],
        'payments' => [],
    ];
    $customLabels = $_POST['service_dynamic_label'] ?? [];
    $customValues = $_POST['service_dynamic_value'] ?? [];
    if (is_array($customLabels) && is_array($customValues)) {
        foreach ($customLabels as $idx => $labelRaw) {
            $label = trim((string)$labelRaw);
            $value = trim((string)($customValues[$idx] ?? ''));
            if ($label === '' || $value === '') {
                continue;
            }
            $payload['custom'][$label] = $value;
        }
    }
    $cloneLabels = $_POST['service_clone_label'] ?? [];
    $cloneValues = $_POST['service_clone_value'] ?? [];
    if (is_array($cloneLabels) && is_array($cloneValues)) {
        foreach ($cloneLabels as $idx => $labelRaw) {
            $label = trim((string)$labelRaw);
            if ($label === '') {
                continue;
            }
            $values = [];
            foreach (preg_split('/[\r\n]+/', (string)($cloneValues[$idx] ?? '')) ?: [] as $line) {
                $line = trim((string)$line);
                if ($line !== '') {
                    $values[] = $line;
                }
            }
            if ($values) {
                $payload['clones'][$label] = $values;
            }
        }
    }
    return $payload;
}
function serviceDynamicPreview(array $payload): string
{
    $parts = [];
    foreach (($payload['custom'] ?? []) as $label => $value) {
        $parts[] = trim((string)$label) . ': ' . trim((string)$value);
    }
    foreach (($payload['clones'] ?? []) as $label => $values) {
        if (!is_array($values) || !$values) {
            continue;
        }
        $parts[] = trim((string)$label) . ': ' . implode(', ', array_map(static fn($v) => trim((string)$v), $values));
    }
    return implode(' | ', $parts);
}
function servicePaymentSummary(array $payload, float $fee): array
{
    $paid = 0.0;
    $currency = '';
    foreach (($payload['payments'] ?? []) as $payment) {
        if (!is_array($payment)) {
            continue;
        }
        $amount = (float)($payment['amount'] ?? 0);
        if ($amount <= 0) {
            continue;
        }
        $paid += $amount;
        if ($currency === '') {
            $currency = strtoupper(trim((string)($payment['currency'] ?? '')));
        }
    }
    return [
        'paid_total' => round($paid, 2),
        'remaining' => round(max(0.0, $fee - $paid), 2),
        'currency' => $currency,
        'payment_count' => is_array($payload['payments'] ?? null) ? count($payload['payments']) : 0,
    ];
}
function agendaDecodeNote($raw): array
{
    $payload = is_array($raw) ? $raw : json_decode((string)$raw, true);
    if (!is_array($payload)) {
        $text = trim((string)$raw);
        return [
            'title' => '',
            'time' => '',
            'note_text' => $text,
            'html' => nl2br(htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')),
            'is_reminder' => false,
            'reminder_at' => '',
        ];
    }
    $text = trim((string)($payload['note_text'] ?? ''));
    if ($text === '' && isset($payload['html'])) {
        $text = trim(strip_tags((string)$payload['html']));
    }
    $title = trim((string)($payload['title'] ?? ''));
    $time = trim((string)($payload['time'] ?? ''));
    $html = trim((string)($payload['html'] ?? ''));
    if ($html === '') {
        $html = nl2br(htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'));
    }
    return [
        'title' => $title,
        'time' => $time,
        'note_text' => $text,
        'html' => $html,
        'is_reminder' => !empty($payload['is_reminder']),
        'reminder_at' => trim((string)($payload['reminder_at'] ?? '')),
    ];
}
function agendaEncodeNote(string $dateKey, string $title, string $time, string $noteText, bool $isReminder): string
{
    $title = trim($title);
    $time = trim($time);
    $noteText = trim($noteText);
    $payload = [
        'html' => nl2br(htmlspecialchars($noteText, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')),
        'is_reminder' => $isReminder,
        'title' => $title,
        'time' => $time,
        'note_text' => $noteText,
        'reminder_at' => ($isReminder && $time !== '' ? ($dateKey . ' ' . $time) : ''),
    ];
    return (string)(json_encode($payload, JSON_UNESCAPED_UNICODE) ?: '{}');
}
function balanceKeyFromItem(array $item, array $supportedCurrencies): ?string
{
    $typ = trim((string)($item['type'] ?? ''));
    if ($typ === '') {
        return null;
    }
    if (isCurrencyType($typ, $supportedCurrencies)) {
        return strtoupper($typ);
    }
    $itemGram = (float)($item['gram'] ?? 0);
    $itemKarat = trim((string)($item['karat'] ?? ''));
    $extras = [];
    if ($itemGram > 0) {
        $extras[] = formatAmountByType($itemGram, 'GRAM', $supportedCurrencies) . ' gr';
    }
    if ($itemKarat !== '') {
        $extras[] = $itemKarat;
    }
    return $extras ? ($typ . ' (' . implode(' ', $extras) . ')') : $typ;
}
function applyHistoryBalanceDelta(array &$balances, array $row, array $supportedCurrencies): void
{
    $currency = trim((string)($row['currency'] ?? ''));
    $desc = trim((string)($row['description'] ?? ''));
    $debt = (float)($row['debt_original'] ?? 0);
    $credit = (float)($row['credit_original'] ?? 0);

    if ($desc !== '' && substr($desc, 0, 1) === '{') {
        $json = json_decode($desc, true);
        if (is_array($json)) {
            $mode = trim((string)($json['mode'] ?? ''));
            $items = $json['exchange_items'] ?? [];
            if (is_array($items) && strpos($mode, 'Exchange') !== false) {
                        foreach ($items as $i => $it) {
                            if (!is_array($it)) {
                                continue;
                            }
                            $amt = (float)($it['amount'] ?? 0);
                            if ($amt <= 0) {
                                continue;
                            }
                            $key = balanceKeyFromItem($it, $supportedCurrencies);
                            if ($key === null) {
                                continue;
                            }
                            if ($mode === 'ExchangePayable') {
                                $sign = ($i === 0) ? -1.0 : 1.0;
                            } elseif ($mode === 'ExchangeReceivable') {
                                $sign = ($i === 0) ? 1.0 : -1.0;
                            } else {
                                $sign = 0.0;
                            }
                            if ($sign !== 0.0) {
                                $balances[$key] = ($balances[$key] ?? 0.0) + ($amt * $sign);
                            }
                        }
                        return;
                    }
                    // Bedelli (Peşin) işlemlerde alınan nakit kasaya gider, müşterinin cari bakiyesine borç/alacak olarak eklenmez.
                    // Bu yüzden "Exchange" (Çeviri) dışındaki işlemlerde JSON exchange_items döngüsü İPTAL EDİLMİŞTİR.
            if (is_array($items) && $items) {
                if (strpos($mode, 'PaidOut') !== false) {
                    $sign = 1.0;
                } elseif (strpos($mode, 'PaidIn') !== false) {
                    $sign = -1.0;
                } else {
                    $sign = 0.0;
                }
                if ($sign !== 0.0) {
                    foreach ($items as $it) {
                        if (!is_array($it)) {
                            continue;
                        }
                        $amt = (float)($it['amount'] ?? 0);
                        if ($amt <= 0) {
                            continue;
                        }
                        $key = balanceKeyFromItem($it, $supportedCurrencies);
                        if ($key === null) {
                            continue;
                        }
                        $balances[$key] = ($balances[$key] ?? 0.0) + ($amt * $sign);
                    }
                    return;
                }
            }
        }
    }

    if ($currency !== '' && $currency !== 'Karma') {
        $balances[$currency] = ($balances[$currency] ?? 0.0) + ($debt - $credit);
    }
}
function parseHistoryBalances(array $events, array $supportedCurrencies): array
{
    $balances = [];
    foreach ($events as $row) {
        applyHistoryBalanceDelta($balances, $row, $supportedCurrencies);
    }
    ksort($balances);
    $rows = [];
    foreach ($balances as $k => $v) {
        if (abs((float)$v) < 0.0001) {
            continue;
        }
        $rows[] = ['currency' => $k, 'net_balance' => (float)$v];
    }
    return $rows;
}
function historyRowsWithRunning(array $events, array $supportedCurrencies): array
{
    if (!$events) {
        return [];
    }
    $chronological = array_reverse($events);
    $running = [];
    $balanceById = [];
    foreach ($chronological as $row) {
        applyHistoryBalanceDelta($running, $row, $supportedCurrencies);
        $balanceById[(string)($row['id'] ?? '')] = $running;
    }
    $out = [];
    foreach ($events as $row) {
        $id = (string)($row['id'] ?? '');
        $curr = $balanceById[$id] ?? [];
        ksort($curr);
        $parts = [];
        foreach ($curr as $typ => $val) {
            if (abs((float)$val) < 0.0001) {
                continue;
            }
            $parts[] = formatAmountByType((float)$val, (string)$typ, $supportedCurrencies) . ' ' . $typ;
        }
        $row['_balance_text'] = $parts ? implode(' | ', $parts) : '0';
        $out[] = $row;
    }
    return $out;
}
function parseAccountKey(string $v): array
{
    $p = explode('|', $v, 2);
    if (count($p) !== 2) {
        return [null, null];
    }
    $type = strtolower(trim($p[0]));
    $id = trim($p[1]);
    if (!in_array($type, ['cash', 'card'], true) || $id === '') {
        return [null, null];
    }
    return [$type, $id];
}
function getTenantPdo(array $master): PDO
{
    $t = $_SESSION['tenant'] ?? [];
    if (!is_array($t)) {
        throw new RuntimeException('Tenant yok.');
    }
    $db = (string)($t['db_name'] ?? '');
    $user = (string)($t['db_user'] ?? '');
    $pass = (string)($t['db_pass'] ?? '');
    if ($db === '' || $user === '') {
        throw new RuntimeException('Tenant bilgisi eksik.');
    }
    return pdoConnect($master['host'], (int)$master['port'], $db, $user, $pass);
}
function syncLogLine(string $message, array $context = []): void
{
    $line = '[' . date('Y-m-d H:i:s') . '] ' . trim($message);
    if ($context) {
        $json = json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($json !== false) {
            $line .= ' ' . $json;
        }
    }
    $line .= PHP_EOL;
    @file_put_contents(__DIR__ . '/sync_events.log', $line, FILE_APPEND);
}
function syncIsSafeTableName(string $tableName): bool
{
    return $tableName !== '' && preg_match('/^[A-Za-z0-9_]+$/', $tableName) === 1;
}
function syncEnsureGlobalEventsTable(PDO $db): void
{
    static $doneByObject = [];
    $objKey = spl_object_id($db);
    if (!empty($doneByObject[$objKey])) {
        return;
    }
    if ($db->inTransaction()) {
        return;
    }
    try {
        $db->exec(
            "CREATE TABLE IF NOT EXISTS global_sync_events (
                id BIGINT AUTO_INCREMENT PRIMARY KEY,
                transaction_uuid CHAR(36),
                device_id VARCHAR(50),
                profile_id CHAR(36),
                table_name VARCHAR(50),
                action_type VARCHAR(20),
                record_id CHAR(36),
                payload_json JSON,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                KEY idx_profile_created (profile_id, created_at),
                KEY idx_tx_uuid (transaction_uuid)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
        );
        $doneByObject[$objKey] = true;
    } catch (Throwable $e) {
        syncLogLine('WEB_SYNC_TABLE_ENSURE_ERROR', ['error' => $e->getMessage()]);
    }
}
function syncFetchRowSnapshot(PDO $db, string $tableName, string $profileId, string $recordId): ?array
{
    if (!syncIsSafeTableName($tableName) || trim($recordId) === '') {
        return null;
    }
    $hasProfile = syncTableHasColumn($db, $tableName, 'profile_id');
    $sql = "SELECT * FROM `{$tableName}` WHERE id = :id";
    $params = [':id' => $recordId];
    if ($hasProfile && trim($profileId) !== '') {
        $sql .= " AND profile_id = :pid";
        $params[':pid'] = $profileId;
    }
    $sql .= " LIMIT 1";
    $st = $db->prepare($sql);
    $st->execute($params);
    $row = $st->fetch();
    return is_array($row) ? $row : null;
}
function syncTableHasColumn(PDO $db, string $tableName, string $columnName): bool
{
    if (!syncIsSafeTableName($tableName) || !syncIsSafeTableName($columnName)) {
        return false;
    }
    try {
        $st = $db->prepare(
            "SELECT 1
             FROM INFORMATION_SCHEMA.COLUMNS
             WHERE TABLE_SCHEMA = DATABASE()
               AND TABLE_NAME = :t
               AND COLUMN_NAME = :c
             LIMIT 1"
        );
        $st->execute([':t' => $tableName, ':c' => $columnName]);
        return (bool)$st->fetchColumn();
    } catch (Throwable $_ignored) {
        return false;
    }
}
function syncEnqueueGlobalEvent(
    PDO $db,
    string $profileId,
    string $tableName,
    string $actionType,
    string $recordId,
    ?array $beforeRow,
    ?array $afterRow,
    array $meta = [],
    ?string $transactionUuid = null,
    string $deviceId = 'WEB'
): void {
    try {
        if (!syncIsSafeTableName($tableName)) {
            return;
        }
        $action = strtoupper(trim($actionType));
        if (!in_array($action, ['ADD', 'UPDATE', 'DELETE', 'ROLLBACK'], true)) {
            return;
        }
        syncEnsureGlobalEventsTable($db);
        $payload = [
            'profile_id' => $profileId,
            'before' => $beforeRow,
            'after' => $afterRow,
            'meta' => $meta,
        ];
        $payloadJson = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($payloadJson === false) {
            $payloadJson = '{}';
        }
        $tx = trim((string)$transactionUuid);
        if ($tx === '') {
            $tx = uuid4();
        }
        $db->prepare(
            "INSERT INTO global_sync_events
            (transaction_uuid, device_id, profile_id, table_name, action_type, record_id, payload_json, created_at)
            VALUES (:tx, :dev, :pid, :tbl, :act, :rid, :payload, NOW())"
        )->execute([
            ':tx' => $tx,
            ':dev' => $deviceId,
            ':pid' => $profileId,
            ':tbl' => $tableName,
            ':act' => $action,
            ':rid' => $recordId,
            ':payload' => $payloadJson,
        ]);
        syncLogLine('WEB_SYNC_EVENT', [
            'profile_id' => $profileId,
            'table' => $tableName,
            'action' => $action,
            'record_id' => $recordId,
            'tx' => $tx,
            'source' => (string)($meta['source'] ?? 'web'),
        ]);
    } catch (Throwable $e) {
        syncLogLine('WEB_SYNC_EVENT_ERROR', [
            'table' => $tableName,
            'action' => $actionType,
            'record_id' => $recordId,
            'error' => $e->getMessage(),
        ]);
    }
}
function pbkdfHash(string $password): string
{
    $salt = random_bytes(16);
    $hash = hash_pbkdf2('sha256', $password, $salt, 100000, 32, true);
    return bin2hex($salt) . '$' . bin2hex($hash);
}
function verifyPbkdf(string $password, string $stored): bool
{
    if (strpos($stored, '$') === false) {
        return false;
    }
    [$saltHex, $hashHex] = explode('$', $stored, 2);
    $salt = @hex2bin($saltHex);
    $exp = @hex2bin($hashHex);
    if ($salt === false || $exp === false) {
        return false;
    }
    $chk = hash_pbkdf2('sha256', $password, $salt, 100000, 32, true);
    return hash_equals($exp, $chk);
}
function isPhpPasswordHash(string $stored): bool
{
    $stored = trim($stored);
    if ($stored === '') {
        return false;
    }
    $info = password_get_info($stored);
    return !empty($info['algo']);
}
function verifyLegacyAware(string $password, string $stored): bool
{
    $stored = trim($stored);
    if ($stored === '') {
        return false;
    }
    if (strlen($stored) === 32 && strpos($stored, '$') === false) {
        return hash_equals(strtolower($stored), strtolower(md5($password)));
    }
    if (verifyPbkdf($password, $stored)) {
        return true;
    }
    if (isPhpPasswordHash($stored)) {
        return password_verify($password, $stored);
    }
    return false;
}
function boolSetting($v): bool
{
    if (is_bool($v)) {
        return $v;
    }
    if (is_int($v) || is_float($v)) {
        return ((int)$v) !== 0;
    }
    $s = strtolower(trim((string)$v));
    if (in_array($s, ['1', 'true', 'yes', 'on', 'y'], true)) {
        return true;
    }
    if (in_array($s, ['0', 'false', 'no', 'off', 'n', 'null', 'none', ''], true)) {
        return false;
    }
    $j = json_decode((string)$v, true);
    if (is_bool($j)) {
        return $j;
    }
    if (is_int($j) || is_float($j)) {
        return ((int)$j) !== 0;
    }
    return false;
}
function normSettingStr($v): string
{
    $s = trim((string)$v);
    if ($s === '') {
        return '';
    }
    if (strlen($s) >= 2 && $s[0] === '"' && $s[strlen($s) - 1] === '"') {
        $j = json_decode($s, true);
        if (is_string($j)) {
            return trim($j);
        }
    }
    return $s;
}
function profilePassState(PDO $db, string $profileId): array
{
    $st = $db->prepare("SELECT setting_key, setting_value FROM app_settings WHERE profile_id=:pid AND setting_key IN ('password_enabled','password_hash')");
    $st->execute([':pid' => $profileId]);
    $enabled = null;
    $hash = '';
    foreach ($st->fetchAll() as $r) {
        if (($r['setting_key'] ?? '') === 'password_enabled') {
            $enabled = $r['setting_value'] ?? null;
        } elseif (($r['setting_key'] ?? '') === 'password_hash') {
            $hash = (string)($r['setting_value'] ?? '');
        }
    }
    return [boolSetting($enabled), normSettingStr($hash)];
}
function hasAdminPassword(PDO $db, string $profileId): bool
{
    [$en, $hash] = profilePassState($db, $profileId);
    return $en && $hash !== '';
}
function hasActiveNonAdminUsers(PDO $db, string $profileId): bool
{
    $st = $db->prepare("SELECT id FROM users WHERE profile_id=:pid AND role<>'admin' AND is_active=1 AND is_deleted=0 LIMIT 1");
    $st->execute([':pid' => $profileId]);
    return (bool)$st->fetch();
}
function countActiveUsers(PDO $db, string $profileId): int
{
    $st = $db->prepare(
        "SELECT COUNT(DISTINCT CONCAT(LOWER(COALESCE(username,'')),'|',LOWER(COALESCE(role,''))))
         FROM users
         WHERE profile_id=:pid AND is_active=1 AND is_deleted=0"
    );
    $st->execute([':pid' => $profileId]);
    return (int)$st->fetchColumn();
}
function getPasswordlessBootstrapAdmin(PDO $db, string $profileId): ?array
{
    if (hasAdminPassword($db, $profileId) || hasActiveNonAdminUsers($db, $profileId)) {
        return null;
    }
    if (countActiveUsers($db, $profileId) !== 1) {
        return null;
    }
    $st = $db->prepare("SELECT id,username,full_name,role,password_hash FROM users WHERE profile_id=:pid AND role='admin' AND is_active=1 AND is_deleted=0 ORDER BY created_at ASC LIMIT 1");
    $st->execute([':pid' => $profileId]);
    $row = $st->fetch();
    if ($row) {
        $hash = trim((string)($row['password_hash'] ?? ''));
        if ($hash !== '') {
            return null;
        }
        return $row;
    }
    return null;
}
function verifyProfilePassword(PDO $db, string $profileId, string $password): bool
{
    [$en, $stored] = profilePassState($db, $profileId);
    if (!$en) {
        return true;
    }
    if ($stored === '') {
        return false;
    }
    $isLegacyMd5 = (strlen($stored) === 32 && strpos($stored, '$') === false);
    $isPhpHash = isPhpPasswordHash($stored);
    if (!verifyLegacyAware($password, $stored)) {
        return false;
    }
    if ($isLegacyMd5 || $isPhpHash) {
        $newHash = pbkdfHash($password);
        $up = $db->prepare("INSERT INTO app_settings (profile_id, setting_key, setting_value, setting_type) VALUES (:pid,:k,:v,:t) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value), setting_type=VALUES(setting_type)");
        $up->execute([':pid' => $profileId, ':k' => 'password_hash', ':v' => $newHash, ':t' => 'str']);
        $db->prepare("UPDATE users SET password_hash=:h WHERE profile_id=:pid AND role='admin' AND is_deleted=0")->execute([':h' => $newHash, ':pid' => $profileId]);
    }
    return true;
}
function ensureDefaultAdmin(PDO $db, string $profileId): void
{
    $st = $db->prepare("SELECT id FROM users WHERE profile_id=:pid AND role='admin' AND is_deleted=0 ORDER BY created_at ASC LIMIT 1");
    $st->execute([':pid' => $profileId]);
    $adminRow = $st->fetch();
    if (!$adminRow) {
        $adminId = uuid4();
        $db->prepare("INSERT INTO users (id, profile_id, username, full_name, password_hash, role, is_active, is_deleted) VALUES (:id,:pid,'admin','Yönetici','', 'admin',1,0)")
            ->execute([':id' => $adminId, ':pid' => $profileId]);
        $afterRow = syncFetchRowSnapshot($db, 'users', $profileId, $adminId);
        syncEnqueueGlobalEvent(
            $db,
            $profileId,
            'users',
            'ADD',
            $adminId,
            null,
            $afterRow,
            ['source' => 'web.ensureDefaultAdmin']
        );
        return;
    }
    if (hasAdminPassword($db, $profileId) || hasActiveNonAdminUsers($db, $profileId) || countActiveUsers($db, $profileId) !== 1) {
        return;
    }
    $adminId = trim((string)($adminRow['id'] ?? ''));
    if ($adminId === '') {
        return;
    }
    $beforeRow = syncFetchRowSnapshot($db, 'users', $profileId, $adminId);
    if (!is_array($beforeRow) || trim((string)($beforeRow['password_hash'] ?? '')) === '') {
        return;
    }
    $db->prepare("UPDATE users SET password_hash='' WHERE id=:id AND profile_id=:pid")->execute([':id' => $adminId, ':pid' => $profileId]);
    $afterRow = syncFetchRowSnapshot($db, 'users', $profileId, $adminId);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'users',
        'UPDATE',
        $adminId,
        $beforeRow,
        $afterRow,
        ['source' => 'web.ensureDefaultAdmin.normalize']
    );
}
function verifyUser(PDO $db, string $userId, string $profileId, string $password): bool
{
    $st = $db->prepare("SELECT password_hash FROM users WHERE id=:id AND profile_id=:pid AND is_active=1 AND is_deleted=0 LIMIT 1");
    $st->execute([':id' => $userId, ':pid' => $profileId]);
    $row = $st->fetch();
    if (!$row) {
        return false;
    }
    $stored = trim((string)($row['password_hash'] ?? ''));
    if ($stored === '') {
        return false;
    }
    $isLegacyMd5 = (strlen($stored) === 32 && strpos($stored, '$') === false);
    $isPhpHash = isPhpPasswordHash($stored);
    if (!verifyLegacyAware($password, $stored)) {
        return false;
    }
    if ($isLegacyMd5 || $isPhpHash) {
        $db->prepare("UPDATE users SET password_hash=:h WHERE id=:id")->execute([':h' => pbkdfHash($password), ':id' => $userId]);
    }
    return true;
}
function hasPerm(array $user, string $perm): bool
{
    if (($user['role'] ?? '') === 'admin') {
        return true;
    }
    $perms = $user['permissions'] ?? [];
    if (!is_array($perms) || $perm === '') {
        return false;
    }
    if (in_array($perm, $perms, true)) {
        return true;
    }

    static $compat = [
        // Shopping compatibility
        'perm_product_purchase' => ['perm_shopping', 'perm_stock_product'],
        'perm_product_sale' => ['perm_shopping', 'perm_stock_product'],
        'perm_scrap_purchase' => ['perm_shopping', 'perm_stock_scrap'],
        'perm_scrap_sale' => ['perm_shopping', 'perm_stock_scrap'],
        'perm_ziynet_purchase' => ['perm_shopping', 'perm_stock_ziynet'],
        'perm_ziynet_sale' => ['perm_shopping', 'perm_stock_ziynet'],

        // Stock compatibility
        'perm_def_product' => ['perm_stock_product'],
        'perm_def_scrap' => ['perm_stock_scrap'],
        'perm_def_ziynet' => ['perm_stock_ziynet'],
        'perm_depots' => ['perm_stock_product', 'perm_stock_scrap', 'perm_stock_ziynet'],

        // Tools/Services compatibility
        'perm_reports' => ['perm_accounts'],
        'perm_agenda' => ['perm_accounts'],
        'perm_search' => ['perm_accounts'],
        'perm_calculator' => ['perm_accounts'],
        'perm_zakat' => ['perm_accounts'],
        'perm_zakat_guest' => ['perm_accounts'],
        'perm_services' => ['perm_accounts'],
        'perm_repair' => ['perm_accounts'],

        // Finance compatibility
        'perm_exchange' => ['perm_transfer'],
        'perm_scheduled' => ['perm_transfer'],
    ];
    foreach ($compat[$perm] ?? [] as $legacyPerm) {
        if (in_array($legacyPerm, $perms, true)) {
            return true;
        }
    }
    return false;
}
function hasAnyPerm(array $user, array $perms): bool
{
    foreach ($perms as $p) {
        if (hasPerm($user, $p)) {
            return true;
        }
    }
    return false;
}
function canAccessAccount(array $user, string $type, string $id): bool
{
    if (($user['role'] ?? '') === 'admin') {
        return true;
    }
    $list = ($type === 'cash') ? ($user['allowed_cash_accounts'] ?? []) : ($user['allowed_card_accounts'] ?? []);
    return is_array($list) && in_array($id, $list, true);
}
function canAccessDepot(array $user, string $depotId): bool
{
    if (($user['role'] ?? '') === 'admin') {
        return true;
    }
    $list = $user['allowed_depots'] ?? [];
    return is_array($list) && in_array($depotId, $list, true);
}
function fetchAccounts(PDO $db, string $profileId, array $user): array
{
    $st = $db->prepare("SELECT id,name,type FROM financial_accounts WHERE profile_id=:pid AND is_deleted=0 AND type IN ('cash','card') ORDER BY type,name");
    $st->execute([':pid' => $profileId]);
    $rows = $st->fetchAll() ?: [];
    if (($user['role'] ?? '') === 'admin') {
        return $rows;
    }
    $out = [];
    foreach ($rows as $r) {
        $t = (string)($r['type'] ?? '');
        $id = (string)($r['id'] ?? '');
        if ($id !== '' && ($t === 'cash' || $t === 'card') && canAccessAccount($user, $t, $id)) {
            $out[] = $r;
        }
    }
    return $out;
}
function fetchDepots(PDO $db, string $profileId, array $user): array
{
    $st = $db->prepare("SELECT id,name FROM depots WHERE profile_id=:pid AND is_deleted=0 ORDER BY name ASC");
    $st->execute([':pid' => $profileId]);
    $rows = $st->fetchAll() ?: [];
    if (($user['role'] ?? '') === 'admin') {
        return $rows;
    }
    $out = [];
    foreach ($rows as $r) {
        $id = (string)($r['id'] ?? '');
        if ($id !== '' && canAccessDepot($user, $id)) {
            $out[] = $r;
        }
    }
    return $out;
}
function getAccount(PDO $db, string $profileId, string $id): ?array
{
    $st = $db->prepare("SELECT id,name,type FROM financial_accounts WHERE id=:id AND profile_id=:pid AND is_deleted=0 LIMIT 1");
    $st->execute([':id' => $id, ':pid' => $profileId]);
    $r = $st->fetch();
    return $r ?: null;
}
function getBalance(PDO $db, string $profileId, string $accountId, string $currency): float
{
    $st = $db->prepare("SELECT COALESCE(SUM(CASE WHEN transaction_type='Income' THEN amount ELSE -amount END),0) AS b FROM financial_transactions WHERE profile_id=:pid AND account_id=:aid AND currency=:cur AND is_deleted=0");
    $st->execute([':pid' => $profileId, ':aid' => $accountId, ':cur' => $currency]);
    $r = $st->fetch();
    return (float)($r['b'] ?? 0);
}
function personIdByName(PDO $db, string $profileId, string $name): ?string
{
    $st = $db->prepare("SELECT id FROM persons WHERE profile_id=:pid AND full_name=:n AND is_deleted=0 LIMIT 1");
    $st->execute([':pid' => $profileId, ':n' => $name]);
    $r = $st->fetch();
    return $r ? (string)$r['id'] : null;
}
function depotIdByName(PDO $db, string $profileId, string $name): ?string
{
    $st = $db->prepare("SELECT id FROM depots WHERE profile_id=:pid AND name=:n AND is_deleted=0 LIMIT 1");
    $st->execute([':pid' => $profileId, ':n' => $name]);
    $r = $st->fetch();
    return $r ? (string)$r['id'] : null;
}
function stockRemoveProduct(PDO $db, string $profileId, string $depotName, string $productName, float $qty): void
{
    if ($qty <= 0) {
        return;
    }
    $depotId = depotIdByName($db, $profileId, $depotName);
    if (!$depotId) {
        throw new RuntimeException('Depo bulunamadı.');
    }
    $st = $db->prepare("SELECT id,quantity FROM stock_items WHERE profile_id=:pid AND depot_id=:did AND LOWER(TRIM(item_type_name))=LOWER(TRIM(:p)) AND stock_type='Product' AND quantity>0 AND is_deleted=0 ORDER BY created_at ASC FOR UPDATE");
    $st->execute([':pid' => $profileId, ':did' => $depotId, ':p' => $productName]);
    $rows = $st->fetchAll() ?: [];
    $total = 0.0;
    foreach ($rows as $r) {
        $total += (float)($r['quantity'] ?? 0);
    }
    if ($total < $qty) {
        throw new RuntimeException("Yetersiz stok. Mevcut {$total}, istenen {$qty}");
    }
    $remaining = $qty;
    foreach ($rows as $r) {
        if ($remaining <= 0.0001) {
            break;
        }
        $rowId = (string)$r['id'];
        $curr = (float)$r['quantity'];
        $beforeRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
        if ($curr > $remaining) {
            $db->prepare("UPDATE stock_items SET quantity=:q WHERE id=:id")->execute([':q' => ($curr - $remaining), ':id' => $rowId]);
            $remaining = 0.0;
        } else {
            $db->prepare("UPDATE stock_items SET quantity=0 WHERE id=:id")->execute([':id' => $rowId]);
            $remaining -= $curr;
        }
        $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
        syncEnqueueGlobalEvent(
            $db,
            $profileId,
            'stock_items',
            'UPDATE',
            $rowId,
            $beforeRow,
            $afterRow,
            ['source' => 'web.stockRemoveProduct']
        );
    }
}
function stockAddProduct(PDO $db, string $profileId, string $depotName, string $productName, float $qty, float $price): void
{
    if ($qty <= 0) {
        return;
    }
    $depotId = depotIdByName($db, $profileId, $depotName);
    if (!$depotId) {
        throw new RuntimeException('Depo bulunamadı.');
    }
    $st = $db->prepare("SELECT id FROM stock_items WHERE profile_id=:pid AND depot_id=:did AND item_type_name=:p AND stock_type='Product' AND ABS(purchase_price_base-:pr)<0.0001 AND (unique_barcode IS NULL OR unique_barcode='') LIMIT 1 FOR UPDATE");
    $st->execute([':pid' => $profileId, ':did' => $depotId, ':p' => $productName, ':pr' => $price]);
    $row = $st->fetch();
    if ($row) {
        $rowId = (string)$row['id'];
        $beforeRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
        $db->prepare("UPDATE stock_items SET quantity=quantity+:q,is_deleted=0,updated_at=NOW() WHERE id=:id")->execute([':q' => $qty, ':id' => $rowId]);
        $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
        syncEnqueueGlobalEvent(
            $db,
            $profileId,
            'stock_items',
            'UPDATE',
            $rowId,
            $beforeRow,
            $afterRow,
            ['source' => 'web.stockAddProduct']
        );
        return;
    }
    $newId = uuid4();
    $db->prepare("INSERT INTO stock_items (id,profile_id,depot_id,stock_type,item_type_name,quantity,purchase_price_base,unique_barcode,is_deleted,created_at,updated_at) VALUES (:id,:pid,:did,'Product',:p,:q,:pr,'',0,NOW(),NOW())")
        ->execute([':id' => $newId, ':pid' => $profileId, ':did' => $depotId, ':p' => $productName, ':q' => $qty, ':pr' => $price]);
    $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $newId);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'stock_items',
        'ADD',
        $newId,
        null,
        $afterRow,
        ['source' => 'web.stockAddProduct']
    );
}
function barcodeExistsInStock(PDO $db, string $profileId, string $barcode): bool
{
    $barcode = trim($barcode);
    if ($barcode === '') {
        return false;
    }
    $st = $db->prepare("SELECT id FROM stock_items WHERE profile_id=:pid AND unique_barcode=:b AND is_deleted=0 AND quantity>0 LIMIT 1");
    $st->execute([':pid' => $profileId, ':b' => $barcode]);
    return (bool)$st->fetch();
}
function barcodeExistsInHistory(PDO $db, string $profileId, string $barcode): bool
{
    $barcode = trim($barcode);
    if ($barcode === '') {
        return false;
    }
    $st = $db->prepare("SELECT id FROM barcode_history WHERE profile_id=:pid AND barcode=:b LIMIT 1");
    $st->execute([':pid' => $profileId, ':b' => $barcode]);
    return (bool)$st->fetch();
}
function stockAddProductBarcode(PDO $db, string $profileId, string $depotName, string $productName, string $barcode, float $gram, float $price): void
{
    $barcode = trim($barcode);
    if ($barcode === '') {
        throw new RuntimeException('Barkod zorunludur.');
    }
    if ($gram <= 0) {
        throw new RuntimeException("Barkod gram bilgisi gecersiz: {$barcode}");
    }
    if (barcodeExistsInStock($db, $profileId, $barcode)) {
        throw new RuntimeException("Bu barkod zaten stokta var: {$barcode}");
    }
    $depotId = depotIdByName($db, $profileId, $depotName);
    if (!$depotId) {
        throw new RuntimeException('Depo bulunamadi.');
    }
    $newId = uuid4();
    $db->prepare("INSERT INTO stock_items (id,profile_id,depot_id,stock_type,item_type_name,quantity,quantity_gr,purchase_price_base,unique_barcode,is_deleted,created_at,updated_at) VALUES (:id,:pid,:did,'Product',:p,1,:gr,:pr,:bc,0,NOW(),NOW())")
        ->execute([':id' => $newId, ':pid' => $profileId, ':did' => $depotId, ':p' => $productName, ':gr' => $gram, ':pr' => $price, ':bc' => $barcode]);
    $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $newId);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'stock_items',
        'ADD',
        $newId,
        null,
        $afterRow,
        ['source' => 'web.stockAddProductBarcode']
    );
}
function stockRemoveProductBarcode(PDO $db, string $profileId, string $depotName, string $productName, string $barcode): void
{
    $barcode = trim($barcode);
    if ($barcode === '') {
        throw new RuntimeException('Barkod zorunludur.');
    }
    $depotId = depotIdByName($db, $profileId, $depotName);
    if (!$depotId) {
        throw new RuntimeException('Depo bulunamadi.');
    }
    $st = $db->prepare("SELECT id,quantity FROM stock_items WHERE profile_id=:pid AND stock_type='Product' AND unique_barcode=:bc AND is_deleted=0 AND quantity>0 LIMIT 1 FOR UPDATE");
    $st->execute([':pid' => $profileId, ':bc' => $barcode]);
    $row = $st->fetch();
    if (!$row) {
        throw new RuntimeException("Bu barkod stokta yok: {$barcode}");
    }
    $rowId = (string)$row['id'];
    $beforeRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
    $db->prepare("UPDATE stock_items SET quantity=0,updated_at=NOW() WHERE id=:id")->execute([':id' => $rowId]);
    $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'stock_items',
        'UPDATE',
        $rowId,
        $beforeRow,
        $afterRow,
        ['source' => 'web.stockRemoveProductBarcode']
    );
}
function stockRemoveByType(PDO $db, string $profileId, string $depotName, string $itemName, string $stockType, float $qty): void
{
    $st = normalizeStockType($stockType);
    if ($st === 'Product') {
        stockRemoveProduct($db, $profileId, $depotName, $itemName, $qty);
        return;
    }
    if ($qty <= 0) {
        return;
    }
    $depotId = depotIdByName($db, $profileId, $depotName);
    if (!$depotId) {
        throw new RuntimeException('Depo bulunamadi.');
    }
    $q = $db->prepare("SELECT id,quantity FROM stock_items WHERE profile_id=:pid AND depot_id=:did AND LOWER(TRIM(item_type_name))=LOWER(TRIM(:n)) AND stock_type=:st AND quantity>0 AND is_deleted=0 ORDER BY created_at ASC FOR UPDATE");
    $q->execute([':pid' => $profileId, ':did' => $depotId, ':n' => $itemName, ':st' => $st]);
    $rows = $q->fetchAll() ?: [];
    $total = 0.0;
    foreach ($rows as $r) {
        $total += (float)($r['quantity'] ?? 0);
    }
    if ($total < $qty) {
        throw new RuntimeException("Yetersiz stok. Mevcut {$total}, istenen {$qty}");
    }
    $remaining = $qty;
    foreach ($rows as $r) {
        if ($remaining <= 0.0001) {
            break;
        }
        $id = (string)$r['id'];
        $curr = (float)$r['quantity'];
        $beforeRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $id);
        if ($curr > $remaining) {
            $db->prepare("UPDATE stock_items SET quantity=:q,updated_at=NOW() WHERE id=:id")->execute([':q' => ($curr - $remaining), ':id' => $id]);
            $remaining = 0.0;
        } else {
            $db->prepare("UPDATE stock_items SET quantity=0,updated_at=NOW() WHERE id=:id")->execute([':id' => $id]);
            $remaining -= $curr;
        }
        $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $id);
        syncEnqueueGlobalEvent(
            $db,
            $profileId,
            'stock_items',
            'UPDATE',
            $id,
            $beforeRow,
            $afterRow,
            ['source' => 'web.stockRemoveByType']
        );
    }
}
function stockAddByType(PDO $db, string $profileId, string $depotName, string $itemName, string $stockType, float $qty, float $price): void
{
    $st = normalizeStockType($stockType);
    if ($st === 'Product') {
        stockAddProduct($db, $profileId, $depotName, $itemName, $qty, $price);
        return;
    }
    if ($qty <= 0) {
        return;
    }
    $depotId = depotIdByName($db, $profileId, $depotName);
    if (!$depotId) {
        throw new RuntimeException('Depo bulunamadi.');
    }
    $q = $db->prepare("SELECT id FROM stock_items WHERE profile_id=:pid AND depot_id=:did AND item_type_name=:n AND stock_type=:st AND ABS(purchase_price_base-:pr)<0.0001 AND (unique_barcode IS NULL OR unique_barcode='') LIMIT 1 FOR UPDATE");
    $q->execute([':pid' => $profileId, ':did' => $depotId, ':n' => $itemName, ':st' => $st, ':pr' => $price]);
    $row = $q->fetch();
    if ($row) {
        $rowId = (string)$row['id'];
        $beforeRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
        $db->prepare("UPDATE stock_items SET quantity=quantity+:q,is_deleted=0,updated_at=NOW() WHERE id=:id")->execute([':q' => $qty, ':id' => $rowId]);
        $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $rowId);
        syncEnqueueGlobalEvent(
            $db,
            $profileId,
            'stock_items',
            'UPDATE',
            $rowId,
            $beforeRow,
            $afterRow,
            ['source' => 'web.stockAddByType']
        );
        return;
    }
    $newId = uuid4();
    $db->prepare("INSERT INTO stock_items (id,profile_id,depot_id,stock_type,item_type_name,quantity,purchase_price_base,unique_barcode,is_deleted,created_at,updated_at) VALUES (:id,:pid,:did,:st,:n,:q,:pr,'',0,NOW(),NOW())")
        ->execute([':id' => $newId, ':pid' => $profileId, ':did' => $depotId, ':st' => $st, ':n' => $itemName, ':q' => $qty, ':pr' => $price]);
    $afterRow = syncFetchRowSnapshot($db, 'stock_items', $profileId, $newId);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'stock_items',
        'ADD',
        $newId,
        null,
        $afterRow,
        ['source' => 'web.stockAddByType']
    );
}
function stockTypeFromDefinition(PDO $db, string $profileId, string $itemName): ?string
{
    $itemName = trim($itemName);
    if ($itemName === '') {
        return null;
    }
    $st = $db->prepare("SELECT stock_type FROM product_definitions WHERE profile_id=:pid AND name=:n AND is_deleted=0 LIMIT 1");
    $st->execute([':pid' => $profileId, ':n' => $itemName]);
    $row = $st->fetch();
    if (!$row) {
        $stNorm = $db->prepare("SELECT stock_type FROM product_definitions WHERE profile_id=:pid AND LOWER(TRIM(name))=LOWER(TRIM(:n)) AND is_deleted=0 LIMIT 1");
        $stNorm->execute([':pid' => $profileId, ':n' => $itemName]);
        $row = $stNorm->fetch();
    }
    if ($row) {
        $stockType = normalizeStockType((string)($row['stock_type'] ?? ''));
        return in_array($stockType, ['Product', 'Ziynet', 'Scrap'], true) ? $stockType : null;
    }
    // Fallback: tanım silinmiş olsa bile stok satırından tipi çıkar.
    $st2 = $db->prepare("SELECT stock_type FROM stock_items WHERE profile_id=:pid AND item_type_name=:n AND is_deleted=0 ORDER BY updated_at DESC, created_at DESC LIMIT 1");
    $st2->execute([':pid' => $profileId, ':n' => $itemName]);
    $row2 = $st2->fetch();
    if (!$row2) {
        $st2Norm = $db->prepare("SELECT stock_type FROM stock_items WHERE profile_id=:pid AND LOWER(TRIM(item_type_name))=LOWER(TRIM(:n)) AND is_deleted=0 ORDER BY updated_at DESC, created_at DESC LIMIT 1");
        $st2Norm->execute([':pid' => $profileId, ':n' => $itemName]);
        $row2 = $st2Norm->fetch();
    }
    if (!$row2) {
        return null;
    }
    $stockType = normalizeStockType((string)($row2['stock_type'] ?? ''));
    return in_array($stockType, ['Product', 'Ziynet', 'Scrap'], true) ? $stockType : null;
}
function applyStockEffects(PDO $db, string $profileId, array $effects, bool $revert = false): void
{
    foreach ($effects as $eff) {
        if (!is_array($eff)) {
            continue;
        }
        $stockType = normalizeStockType((string)($eff['stock_type'] ?? 'Product'));
        $itemName = trim((string)($eff['item_name'] ?? ''));
        $depot = trim((string)($eff['depot'] ?? ui_text('default_depot_name', 'Merkez', 'Central')));
        $qty = (float)($eff['qty'] ?? 0);
        $price = (float)($eff['price'] ?? 0);
        $direction = strtolower(trim((string)($eff['direction'] ?? '')));
        if ($itemName === '' || $qty <= 0 || !in_array($direction, ['in', 'out'], true)) {
            continue;
        }
        $d = $revert ? ($direction === 'in' ? 'out' : 'in') : $direction;
        $barcodeRows = $eff['barcodes'] ?? [];
        if ($stockType === 'Product' && is_array($barcodeRows) && $barcodeRows) {
            foreach ($barcodeRows as $br) {
                if (!is_array($br)) {
                    continue;
                }
                $bc = trim((string)($br['barcode'] ?? ''));
                $gr = (float)($br['gram'] ?? 0);
                if ($bc === '') {
                    continue;
                }
                if ($d === 'out') {
                    stockRemoveProductBarcode($db, $profileId, $depot, $itemName, $bc);
                } else {
                    stockAddProductBarcode($db, $profileId, $depot, $itemName, $bc, ($gr > 0 ? $gr : 0.001), $price);
                }
            }
            continue;
        }
        if ($d === 'out') {
            stockRemoveByType($db, $profileId, $depot, $itemName, $stockType, $qty);
        } else {
            stockAddByType($db, $profileId, $depot, $itemName, $stockType, $qty, $price);
        }
    }
}
function extractStockEffectsFromDesc(string $description): array
{
    $description = trim($description);
    if ($description === '' || substr($description, 0, 1) !== '{') {
        return [];
    }
    $json = json_decode($description, true);
    if (!is_array($json)) {
        return [];
    }
    $effects = $json['stock_effects'] ?? [];
    return is_array($effects) ? $effects : [];
}
function isHexColor(string $v): bool
{
    return (bool)preg_match('/^#[0-9a-fA-F]{6}$/', trim($v));
}
function hexToRgb(string $hex): array
{
    $hex = ltrim(trim($hex), '#');
    return [hexdec(substr($hex, 0, 2)), hexdec(substr($hex, 2, 2)), hexdec(substr($hex, 4, 2))];
}
function rgbToHex(array $rgb): string
{
    $r = max(0, min(255, (int)round((float)($rgb[0] ?? 0))));
    $g = max(0, min(255, (int)round((float)($rgb[1] ?? 0))));
    $b = max(0, min(255, (int)round((float)($rgb[2] ?? 0))));
    return sprintf('#%02X%02X%02X', $r, $g, $b);
}
function mixHex(string $a, string $b, float $ratio): string
{
    if (!isHexColor($a) || !isHexColor($b)) {
        return '#CCCCCC';
    }
    $ratio = max(0.0, min(1.0, $ratio));
    [$ar, $ag, $ab] = hexToRgb($a);
    [$br, $bg, $bb] = hexToRgb($b);
    return rgbToHex([
        $ar + (($br - $ar) * $ratio),
        $ag + (($bg - $ag) * $ratio),
        $ab + (($bb - $ab) * $ratio),
    ]);
}
function colorLightness(string $hex): float
{
    [$r, $g, $b] = hexToRgb($hex);
    return ($r + $g + $b) / 3.0;
}
function lightenHex(string $hex, float $ratio): string
{
    return mixHex($hex, '#FFFFFF', $ratio);
}
function darkenHex(string $hex, float $ratio): string
{
    return mixHex($hex, '#000000', $ratio);
}
function themeCatalogBuiltin(): array
{
    return [
        'Golder Black' => [
            'primary' => '#B88A3B',
            'secondary' => '#1B1B1B',
            'background' => '#090909',
            'background_alt' => '#141414',
            'text_dark' => '#F5F5F5',
            'text_light' => '#FFFFFF',
            'tertiary' => '#232323',
            'border' => '#2A2A2A',
            'danger' => '#E06C6C',
            'warning' => '#D4A451',
        ],
        'Green Harmony' => [
            'primary' => '#285430',
            'secondary' => '#5F8D4E',
            'background' => '#F1F8E9',
            'text_dark' => '#000000',
            'text_light' => '#FFFFFF',
            'danger' => '#D32F2F',
            'warning' => '#FFC107',
        ],
        'Modern Dark Gold' => [
            'primary' => '#D4AF37',
            'secondary' => '#4F4F4F',
            'background' => '#212121',
            'background_alt' => '#333333',
            'text_dark' => '#FFFFFF',
            'text_light' => '#FFFFFF',
            'tertiary' => '#6E6E6E',
            'border' => '#555555',
            'danger' => '#CF6679',
            'warning' => '#FFCA28',
        ],
        'Ocean Blue' => [
            'primary' => '#0D47A1',
            'secondary' => '#1976D2',
            'background' => '#E3F2FD',
            'text_dark' => '#000000',
            'text_light' => '#FFFFFF',
            'danger' => '#D32F2F',
            'warning' => '#FFC107',
        ],
        'Royal Purple' => [
            'primary' => '#4A148C',
            'secondary' => '#7B1FA2',
            'background' => '#F3E5F5',
            'text_dark' => '#000000',
            'text_light' => '#FFFFFF',
            'danger' => '#D32F2F',
            'warning' => '#FFC107',
        ],
        'Ruby Red' => [
            'primary' => '#B71C1C',
            'secondary' => '#D32F2F',
            'background' => '#FFEBEE',
            'text_dark' => '#000000',
            'text_light' => '#FFFFFF',
            'danger' => '#B71C1C',
            'warning' => '#FFC107',
        ],
        'Sunset Orange' => [
            'primary' => '#E65100',
            'secondary' => '#F57C00',
            'background' => '#FFF3E0',
            'text_dark' => '#000000',
            'text_light' => '#FFFFFF',
            'danger' => '#D32F2F',
            'warning' => '#E65100',
        ],
        'Golden Sun' => [
            'primary' => '#F57F17',
            'secondary' => '#FBC02D',
            'background' => '#FFFDE7',
            'text_dark' => '#000000',
            'text_light' => '#000000',
            'danger' => '#D32F2F',
            'warning' => '#F57F17',
        ],
        'Classic Dark' => [
            'primary' => '#212121',
            'secondary' => '#424242',
            'background' => '#303030',
            'text_dark' => '#FFFFFF',
            'text_light' => '#FFFFFF',
            'danger' => '#E53935',
            'warning' => '#FFB300',
        ],
    ];
}
function normalizeThemeDefinition(array $raw): ?array
{
    $required = ['primary', 'secondary', 'background', 'text_dark', 'text_light', 'danger', 'warning'];
    $theme = [];
    foreach ($required as $k) {
        $v = strtoupper(trim((string)($raw[$k] ?? '')));
        if (!isHexColor($v)) {
            return null;
        }
        $theme[$k] = $v;
    }
    foreach (['background_alt', 'tertiary', 'border'] as $optional) {
        $v = strtoupper(trim((string)($raw[$optional] ?? '')));
        if ($v !== '' && isHexColor($v)) {
            $theme[$optional] = $v;
        }
    }
    return $theme;
}
function decodeThemeCollectionFromJson(string $raw): array
{
    $raw = trim($raw);
    if ($raw === '') {
        return [];
    }
    $decoded = json_decode($raw, true);
    if (is_string($decoded)) {
        $decoded = json_decode($decoded, true);
    }
    if (!is_array($decoded)) {
        return [];
    }
    $out = [];
    foreach ($decoded as $name => $themeRaw) {
        $themeName = trim((string)$name);
        if ($themeName === '' || !is_array($themeRaw)) {
            continue;
        }
        $normalized = normalizeThemeDefinition($themeRaw);
        if ($normalized === null) {
            continue;
        }
        $out[$themeName] = $normalized;
    }
    return $out;
}
function customThemesFromDb(PDO $db, string $profileId): array
{
    try {
        $st = $db->prepare("SELECT setting_value FROM app_settings WHERE profile_id=:pid AND setting_key='custom_themes_json' LIMIT 1");
        $st->execute([':pid' => $profileId]);
        $row = $st->fetch();
        if (!$row) {
            return [];
        }
        return decodeThemeCollectionFromJson((string)($row['setting_value'] ?? ''));
    } catch (Throwable $ex) {
        return [];
    }
}
function resolveThemeVarsFromName(string $themeName, array $customThemes = []): array
{
    $themes = themeCatalogBuiltin();
    foreach ($customThemes as $name => $theme) {
        if (!isset($themes[$name]) && is_array($theme)) {
            $themes[$name] = $theme;
        }
    }
    $t = $themes[$themeName] ?? $themes['Green Harmony'];
    $bg = (string)$t['background'];
    $primary = (string)$t['primary'];
    $secondary = (string)$t['secondary'];
    $text = (string)$t['text_dark'];
    $textLight = (string)($t['text_light'] ?? '#FFFFFF');
    $err = (string)$t['danger'];
    $warning = (string)($t['warning'] ?? $secondary);
    $isDark = colorLightness($bg) < 140;
    $backgroundAlt = (string)($t['background_alt'] ?? (colorLightness($bg) < 230 ? lightenHex($bg, 0.03) : darkenHex($bg, 0.03)));
    $tertiary = (string)($t['tertiary'] ?? lightenHex($secondary, 0.2));
    $border = (string)($t['border'] ?? darkenHex($bg, 0.1));
    $muted = $isDark ? mixHex($text, $bg, 0.45) : mixHex($text, '#FFFFFF', 0.45);
    return [
        'bg' => $bg,
        'bg2' => $isDark ? darkenHex($bg, 0.15) : lightenHex($bg, 0.12),
        'card' => $backgroundAlt,
        'line' => $border,
        'text' => $text,
        'muted' => $muted,
        'ok' => '#067647',
        'okbg' => '#ECFDF3',
        'err' => $err,
        'errbg' => '#FEF3F2',
        'primary' => $primary,
        'primary2' => $secondary,
        'secondary' => $secondary,
        'tertiary' => $tertiary,
        'background' => $bg,
        'background_alt' => $backgroundAlt,
        'border' => $border,
        'text_dark' => $text,
        'text_light' => $textLight,
        'danger' => $err,
        'warning' => $warning,
    ];
}
function profileThemeVars(PDO $db, string $profileId): array
{
    $themeName = 'Green Harmony';
    $customThemes = customThemesFromDb($db, $profileId);
    try {
        $st = $db->prepare("SELECT setting_value FROM app_settings WHERE profile_id=:pid AND setting_key='theme_name' LIMIT 1");
        $st->execute([':pid' => $profileId]);
        $row = $st->fetch();
        if ($row) {
            $raw = trim((string)($row['setting_value'] ?? ''));
            $decoded = json_decode($raw, true);
            if (is_string($decoded) && trim($decoded) !== '') {
                $themeName = trim($decoded);
            } elseif ($raw !== '') {
                $themeName = trim($raw, "\" \t\n\r\0\x0B");
            }
        }
    } catch (Throwable $ex) {
    }
    return resolveThemeVarsFromName($themeName, $customThemes);
}
function normalizeRateKey(string $key): string
{
    $k = trim($key);
    if ($k === '') {
        return '';
    }
    $lower = function_exists('mb_strtolower') ? mb_strtolower($k, 'UTF-8') : strtolower($k);
    $lower = strtr($lower, [
        'ı' => 'i', 'İ' => 'i',
        'ş' => 's', 'ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€šÃ‚Â¦ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â' => 's',
        'ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€šÃ‚ÂÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â¦ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¸' => 'g', 'ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€šÃ‚ÂÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â' => 'g',
        'ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€Â¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¼' => 'u', 'ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€Â¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â¦ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€¦Ã¢â‚¬Å“' => 'u',
        'ö' => 'o', 'ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€Â¢ÃƒÆ’Ã†â€™Ãƒâ€šÃ‚Â¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡Ãƒâ€šÃ‚Â¬ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€¦Ã¢â‚¬Å“' => 'o',
        'ç' => 'c', 'ç' => 'c',
    ]);
    $ascii = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $lower);
    if ($ascii === false) {
        $ascii = $lower;
    }
    return preg_replace('/[^a-z]/', '', $ascii) ?? '';
}
function extractRateBuySell(array $item): array
{
    $buy = '';
    $sell = '';
    foreach ($item as $rawKey => $rawVal) {
        $k = normalizeRateKey((string)$rawKey);
        if ($buy === '' && in_array($k, ['alis', 'buy', 'buying', 'bid'], true)) {
            $buy = trim((string)$rawVal);
        }
        if ($sell === '' && in_array($k, ['satis', 'sell', 'selling', 'ask'], true)) {
            $sell = trim((string)$rawVal);
        }
    }
    if ($buy === '') {
        foreach ($item as $rawKey => $rawVal) {
            $k = normalizeRateKey((string)$rawKey);
            if (str_starts_with($k, 'al') || str_starts_with($k, 'buy') || str_starts_with($k, 'bid')) {
                $buy = trim((string)$rawVal);
                break;
            }
        }
    }
    if ($sell === '') {
        foreach ($item as $rawKey => $rawVal) {
            $k = normalizeRateKey((string)$rawKey);
            if (str_starts_with($k, 'sat') || str_starts_with($k, 'sel') || str_starts_with($k, 'ask')) {
                $sell = trim((string)$rawVal);
                break;
            }
        }
    }
    return [$buy, $sell];
}
function rateItemLabel(string $key): string
{
    $map = [
        'USD' => 'USD',
        'EUR' => 'EUR',
        'GBP' => 'GBP',
        'gram-altin' => 'Gram Altın',
        'ceyrek-altin' => 'çeyrek Altın',
        'yarim-altin' => 'Yarım Altın',
        'tam-altin' => 'Tam Altın',
        'cumhuriyet-altini' => 'Cumhuriyet Altını',
        '22-ayar-bilezik' => '22 Ayar Bilezik',
    ];
    return $map[$key] ?? str_replace('-', ' ', $key);
}
function fetchCachedRatePayload(PDO $db, string $profileId): array
{
    $st = $db->prepare("SELECT setting_key,setting_value FROM app_settings WHERE profile_id=:pid AND setting_key IN ('web_rates_cache_json','web_rates_cache_at')");
    $st->execute([':pid' => $profileId]);
    $cacheJson = '';
    $cacheAt = 0;
    foreach ($st->fetchAll() ?: [] as $row) {
        $k = (string)($row['setting_key'] ?? '');
        $v = (string)($row['setting_value'] ?? '');
        if ($k === 'web_rates_cache_json') {
            $cacheJson = $v;
        } elseif ($k === 'web_rates_cache_at') {
            $cacheAt = (int)$v;
        }
    }
    $decoded = json_decode($cacheJson, true);
    if (!is_array($decoded)) {
        $decoded = [];
    }
    return [$decoded, $cacheAt];
}
function saveRatePayloadCache(PDO $db, string $profileId, array $payload): void
{
    if (!profileRowById($db, $profileId)) {
        return;
    }
    $json = json_encode($payload, JSON_UNESCAPED_UNICODE);
    if (!is_string($json) || $json === '') {
        return;
    }
    $now = (string)time();
    $up = $db->prepare("INSERT INTO app_settings (profile_id,setting_key,setting_value,setting_type) VALUES (:pid,:k,:v,:t) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value),setting_type=VALUES(setting_type)");
    $up->execute([':pid' => $profileId, ':k' => 'web_rates_cache_json', ':v' => $json, ':t' => 'json']);
    $up->execute([':pid' => $profileId, ':k' => 'web_rates_cache_at', ':v' => $now, ':t' => 'int']);
}
function decodeRatePayload(string $raw): array
{
    $decoded = json_decode($raw, true);
    return is_array($decoded) ? $decoded : [];
}
function fetchDashboardRates(PDO $db, string $profileId): array
{
    [$cached, $cacheAt] = fetchCachedRatePayload($db, $profileId);
    $ttlSec = 900;
    if ($cached && $cacheAt > 0 && (time() - $cacheAt) < $ttlSec) {
        return $cached;
    }
    $localPaths = [
        dirname(__DIR__) . DIRECTORY_SEPARATOR . 'kurlar' . DIRECTORY_SEPARATOR . 'kurlar.json',
        '/var/www/html/kurlar/kurlar.json',
    ];
    foreach ($localPaths as $localPath) {
        if (!is_file($localPath) || !is_readable($localPath)) {
            continue;
        }
        $raw = @file_get_contents($localPath);
        if (!is_string($raw) || $raw === '') {
            continue;
        }
        $decoded = decodeRatePayload($raw);
        if ($decoded) {
            saveRatePayloadCache($db, $profileId, $decoded);
            return $decoded;
        }
    }
    $url = 'https://golder.biz/kurlar/kurlar.json';
    $context = stream_context_create([
        'http' => [
            'method' => 'GET',
            'timeout' => 8,
            'header' => "User-Agent: GolderWeb/1.19\r\nAccept: application/json\r\n",
        ],
        'ssl' => [
            'verify_peer' => false,
            'verify_peer_name' => false,
        ],
    ]);
    $raw = @file_get_contents($url, false, $context);
    if ($raw !== false && $raw !== '') {
        $decoded = decodeRatePayload($raw);
        if (is_array($decoded) && $decoded) {
            saveRatePayloadCache($db, $profileId, $decoded);
            return $decoded;
        }
    }
    return $cached;
}
function dashboardRateRows(array $rates): array
{
    if (!$rates) {
        return [];
    }
    $keys = ['USD', 'EUR', 'GBP', 'gram-altin', 'ceyrek-altin', 'yarim-altin', 'tam-altin', 'cumhuriyet-altini', '22-ayar-bilezik'];
    $rows = [];
    foreach ($keys as $k) {
        if (!isset($rates[$k]) || !is_array($rates[$k])) {
            continue;
        }
        [$buy, $sell] = extractRateBuySell($rates[$k]);
        $rows[] = [
            'name' => rateItemLabel($k),
            'buy' => $buy,
            'sell' => $sell,
        ];
    }
    return $rows;
}
function addFinTrans(PDO $db, string $profileId, string $accId, string $dateSql, string $type, float $amount, string $currency, float $baseAmount, string $desc, ?string $personId, ?string $relatedId): string
{
    $id = uuid4();
    $db->prepare("INSERT INTO financial_transactions (id,profile_id,account_id,transaction_date,transaction_type,amount,currency,base_amount,description,related_person_id,related_record_id,is_deleted,created_at,updated_at) VALUES (:id,:pid,:aid,:d,:t,:a,:c,:b,:desc,:person,:rel,0,NOW(),NOW())")
        ->execute([
            ':id' => $id,
            ':pid' => $profileId,
            ':aid' => $accId,
            ':d' => $dateSql,
            ':t' => $type,
            ':a' => $amount,
            ':c' => $currency,
            ':b' => $baseAmount,
            ':desc' => $desc,
            ':person' => $personId,
            ':rel' => $relatedId,
        ]);
    $afterRow = syncFetchRowSnapshot($db, 'financial_transactions', $profileId, $id);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'financial_transactions',
        'ADD',
        $id,
        null,
        $afterRow,
        ['source' => 'web.addFinTrans']
    );
    return $id;
}
function addHist(PDO $db, string $profileId, ?string $personId, string $personName, string $eventDate, string $eventType, string $desc, float $debt, float $credit, ?string $currency, ?string $relatedId): string
{
    $id = uuid4();
    $db->prepare("INSERT INTO customer_history_events (id,profile_id,person_id,person_full_name,event_date,event_type,description,debt_original,credit_original,currency,transaction_amount,related_id,created_at) VALUES (:id,:pid,:person_id,:person,:ed,:et,:d,:debt,:credit,:cur,0,:rel,NOW())")
        ->execute([
            ':id' => $id,
            ':pid' => $profileId,
            ':person_id' => $personId,
            ':person' => $personName,
            ':ed' => $eventDate,
            ':et' => $eventType,
            ':d' => $desc,
            ':debt' => $debt,
            ':credit' => $credit,
            ':cur' => $currency,
            ':rel' => $relatedId,
        ]);
    $afterRow = syncFetchRowSnapshot($db, 'customer_history_events', $profileId, $id);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'customer_history_events',
        'ADD',
        $id,
        null,
        $afterRow,
        ['source' => 'web.addHist']
    );
    return $id;
}
function settingUpsert(PDO $db, string $profileId, string $key, string $value, string $type = 'str'): void
{
    if (!profileRowById($db, $profileId)) {
        return;
    }
    $beforeRow = fetchSettingRow($db, $profileId, $key);
    $db->prepare("INSERT INTO app_settings (profile_id,setting_key,setting_value,setting_type) VALUES (:pid,:k,:v,:t) ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value),setting_type=VALUES(setting_type)")
        ->execute([':pid' => $profileId, ':k' => $key, ':v' => $value, ':t' => $type]);
    $afterRow = fetchSettingRow($db, $profileId, $key);
    syncEnqueueGlobalEvent(
        $db,
        $profileId,
        'app_settings',
        ($beforeRow ? 'UPDATE' : 'ADD'),
        settingRecordId($profileId, $key),
        $beforeRow,
        $afterRow,
        ['source' => 'web.settingUpsert', 'setting_key' => $key]
    );
}
function settingRecordId(string $profileId, string $key): string
{
    $seed = strtolower(trim($profileId) . ':' . trim($key));
    $h = substr(sha1($seed), 0, 32);
    return substr($h, 0, 8) . '-' . substr($h, 8, 4) . '-' . substr($h, 12, 4) . '-' . substr($h, 16, 4) . '-' . substr($h, 20, 12);
}
function profileRowById(PDO $db, string $profileId): ?array
{
    $profileId = trim($profileId);
    if ($profileId === '') {
        return null;
    }
    $st = $db->prepare("SELECT id,name,is_active,is_deleted FROM profiles WHERE id=:id AND is_active=1 AND is_deleted=0 LIMIT 1");
    $st->execute([':id' => $profileId]);
    $row = $st->fetch();
    return is_array($row) ? $row : null;
}
function fetchSettingRow(PDO $db, string $profileId, string $key): ?array
{
    $st = $db->prepare(
        "SELECT profile_id, setting_key, setting_value, setting_type
         FROM app_settings
         WHERE profile_id = :pid AND setting_key = :k
         LIMIT 1"
    );
    $st->execute([':pid' => $profileId, ':k' => $key]);
    $row = $st->fetch();
    return is_array($row) ? $row : null;
}
function ensureStage5Tables(PDO $db): void
{
    $queries = [
        "CREATE TABLE IF NOT EXISTS sales (
            id CHAR(36) PRIMARY KEY,
            profile_id CHAR(36),
            sale_date DATE,
            customer_full_name VARCHAR(150),
            total_amount DECIMAL(19, 4) DEFAULT 0,
            currency VARCHAR(10),
            payment_method_summary TEXT,
            products_json LONGTEXT,
            is_deleted TINYINT(1) DEFAULT 0,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS repair_records (
            id CHAR(36) PRIMARY KEY,
            profile_id CHAR(36),
            customer_full_name VARCHAR(150),
            product_description TEXT,
            complaint_request TEXT,
            receipt_date DATE,
            estimated_fee DECIMAL(19, 4) DEFAULT 0,
            fee_currency VARCHAR(10),
            status VARCHAR(50),
            delivery_date DATE,
            notes TEXT,
            is_deleted TINYINT(1) DEFAULT 0,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS service_definitions (
            id CHAR(36) PRIMARY KEY,
            profile_id CHAR(36),
            name VARCHAR(100),
            custom_columns_json LONGTEXT,
            clone_columns_json LONGTEXT
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS service_jobs (
            id CHAR(36) PRIMARY KEY,
            profile_id CHAR(36),
            service_def_id CHAR(36),
            customer_name VARCHAR(150),
            description TEXT,
            status VARCHAR(50),
            fee DECIMAL(19, 4) DEFAULT 0,
            currency VARCHAR(10),
            start_date DATE,
            delivery_date DATE,
            dynamic_data_json LONGTEXT
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS agenda_notes (
            profile_id CHAR(36),
            note_key VARCHAR(100),
            note_value LONGTEXT,
            PRIMARY KEY (profile_id, note_key),
            INDEX idx_profile (profile_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS taxes (
            id CHAR(36) PRIMARY KEY,
            profile_id CHAR(36) NOT NULL,
            name VARCHAR(120) NOT NULL,
            rate DECIMAL(19, 4) NOT NULL DEFAULT 0,
            type VARCHAR(10) NOT NULL,
            country VARCHAR(2) DEFAULT NULL,
            active TINYINT(1) DEFAULT 1,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            INDEX idx_taxes_profile (profile_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS tax_profiles (
            id CHAR(36) PRIMARY KEY,
            profile_id CHAR(36) NOT NULL,
            name VARCHAR(120) NOT NULL,
            description TEXT,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            INDEX idx_tax_profiles_profile (profile_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS tax_profile_items (
            id CHAR(36) PRIMARY KEY,
            profile_id CHAR(36) NOT NULL,
            tax_profile_id CHAR(36) NOT NULL,
            tax_id CHAR(36) NOT NULL,
            priority INT NOT NULL DEFAULT 0,
            enabled_by_default TINYINT(1) DEFAULT 1,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            UNIQUE KEY uniq_profile_tax (tax_profile_id, tax_id),
            INDEX idx_tax_profile_items_profile (profile_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS tax_applications (
            id CHAR(36) PRIMARY KEY,
            source_table VARCHAR(60) NOT NULL,
            source_id CHAR(36) NOT NULL,
            profile_id CHAR(36) NOT NULL,
            subtotal DECIMAL(19, 4) NOT NULL DEFAULT 0,
            tax_total DECIMAL(19, 4) NOT NULL DEFAULT 0,
            grand_total DECIMAL(19, 4) NOT NULL DEFAULT 0,
            currency VARCHAR(10) NOT NULL,
            snapshot_json LONGTEXT,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            INDEX idx_tax_app_source (source_table, source_id),
            INDEX idx_tax_app_profile (profile_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS global_sync_events (
            id BIGINT AUTO_INCREMENT PRIMARY KEY,
            transaction_uuid CHAR(36),
            device_id VARCHAR(50),
            profile_id CHAR(36),
            table_name VARCHAR(50),
            action_type VARCHAR(20),
            record_id CHAR(36),
            payload_json JSON,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            KEY idx_profile_created (profile_id, created_at),
            KEY idx_tx_uuid (transaction_uuid)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
        "CREATE TABLE IF NOT EXISTS deleted_profiles (
            profile_id CHAR(36) PRIMARY KEY,
            profile_name VARCHAR(100) NULL,
            transaction_uuid CHAR(36) NULL,
            device_id VARCHAR(50) NULL,
            deleted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            KEY idx_deleted_profiles_deleted_at (deleted_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;",
    ];
    foreach ($queries as $sql) {
        try {
            $db->exec($sql);
        } catch (Throwable $_ignored) {
        }
    }
    try {
        if (!syncTableHasColumn($db, 'service_definitions', 'clone_columns_json')) {
            $db->exec("ALTER TABLE service_definitions ADD COLUMN clone_columns_json LONGTEXT NULL AFTER custom_columns_json");
        }
    } catch (Throwable $_ignored) {
    }
}
function rateTextToFloat(?string $raw): float
{
    $v = trim((string)$raw);
    if ($v === '') {
        return 0.0;
    }
    $v = str_replace([' ', "\xc2\xa0"], '', $v);
    if (substr_count($v, ',') > 0 && substr_count($v, '.') > 0) {
        $v = str_replace('.', '', $v);
    }
    $v = str_replace(',', '.', $v);
    return is_numeric($v) ? (float)$v : 0.0;
}
function currencyRateToTry(string $currency, array $ratesPayload): ?float
{
    $cur = strtoupper(trim($currency));
    if ($cur === 'TRY') {
        return 1.0;
    }
    if ($cur === '') {
        return null;
    }
    $rateRow = $ratesPayload[$cur] ?? $ratesPayload[strtolower($cur)] ?? null;
    if (!is_array($rateRow)) {
        return null;
    }
    [$buy, $sell] = extractRateBuySell($rateRow);
    $buyF = rateTextToFloat($buy);
    $sellF = rateTextToFloat($sell);
    if ($buyF > 0) {
        return $buyF;
    }
    if ($sellF > 0) {
        return $sellF;
    }
    return null;
}
function convertCurrencyAmount(float $amount, string $fromCurrency, string $toCurrency, array $ratesPayload): ?float
{
    $from = strtoupper(trim($fromCurrency));
    $to = strtoupper(trim($toCurrency));
    if ($from === '' || $to === '') {
        return null;
    }
    if ($from === $to) {
        return $amount;
    }
    $fromToTry = currencyRateToTry($from, $ratesPayload);
    $toToTry = currencyRateToTry($to, $ratesPayload);
    if ($fromToTry === null || $toToTry === null || $fromToTry <= 0 || $toToTry <= 0) {
        return null;
    }
    $tryAmount = $amount * $fromToTry;
    return $tryAmount / $toToTry;
}
function fetchActiveTaxesByIds(PDO $db, string $profileId, array $rawIds): array
{
    return golder_fetch_active_taxes_by_ids($db, $profileId, $rawIds);
}
function computeTaxSnapshot(
    float $subtotal,
    string $currency,
    array $selectedTaxes,
    string $customerName = '',
    string $note = '',
    array $lines = []
): array {
    $items = (is_array($lines) && $lines)
        ? $lines
        : [[
            'name' => trim($note) !== '' ? $note : 'Kalem',
            'qty' => 1,
            'unit_price' => $subtotal,
        ]];
    return golder_compute_tax_snapshot($items, $currency, $selectedTaxes, $customerName, $note, true);
}
function buildReportDataset(
    PDO $db,
    string $profileId,
    string $reportType,
    string $reportStart,
    string $reportEnd,
    array $supportedCurrencies,
    string $customerName = '',
    string $depotName = '',
    array $allowedDepotNames = []
): array {
    $reportType = trim($reportType);
    $customerName = trim($customerName);
    $depotName = trim($depotName);
    $allowedDepotMap = [];
    foreach ($allowedDepotNames as $dn) {
        $name = trim((string)$dn);
        if ($name !== '') {
            $allowedDepotMap[strtolower($name)] = true;
        }
    }

    if ($reportType === 'cashflow') {
        $title = ui_text('web_report_cashflow', 'Nakit Akışı', 'Cash Flow');
        $headers = [
            ui_text('report_header_date', 'Tarih', 'Date'),
            ui_text('report_header_account', 'Hesap', 'Account'),
            ui_text('report_header_type', 'Tip', 'Type'),
            ui_text('report_header_currency', 'PB', 'Currency'),
            ui_text('report_header_amount', 'Tutar', 'Amount'),
            ui_text('report_header_description', 'Açıklama', 'Description'),
        ];
        $rows = [];
        $sumIn = 0.0;
        $sumOut = 0.0;
        $st = $db->prepare(
            "SELECT ft.transaction_date, fa.name account_name, ft.transaction_type, ft.currency, ft.amount, ft.description
             FROM financial_transactions ft
             INNER JOIN financial_accounts fa ON fa.id=ft.account_id AND fa.profile_id=ft.profile_id
             WHERE ft.profile_id=:pid AND ft.is_deleted=0 AND ft.transaction_date BETWEEN :d1 AND :d2
             ORDER BY ft.transaction_date DESC, ft.created_at DESC"
        );
        $st->execute([':pid' => $profileId, ':d1' => $reportStart, ':d2' => $reportEnd]);
        foreach ($st->fetchAll() ?: [] as $r) {
            $rows[] = [
                trDate((string)$r['transaction_date']),
                (string)($r['account_name'] ?? ''),
                ((string)($r['transaction_type'] ?? '') === 'Income'
                    ? ui_text('entry_income', 'Gelir', 'Income')
                    : ui_text('exit_expense', 'Gider', 'Expense')),
                (string)($r['currency'] ?? 'TRY'),
                formatAmountByType((float)($r['amount'] ?? 0), (string)($r['currency'] ?? 'TRY'), $supportedCurrencies),
                (string)($r['description'] ?? ''),
            ];
            if ((string)($r['transaction_type'] ?? '') === 'Income') {
                $sumIn += (float)($r['amount'] ?? 0);
            } else {
                $sumOut += (float)($r['amount'] ?? 0);
            }
        }
        return [
            'title' => $title,
            'headers' => $headers,
            'rows' => $rows,
            'summary' => [
                ui_text('report_summary_income', 'Toplam Gelir', 'Total Income') => formatAmountByType($sumIn, 'TRY', $supportedCurrencies),
                ui_text('report_summary_expense', 'Toplam Gider', 'Total Expense') => formatAmountByType($sumOut, 'TRY', $supportedCurrencies),
                ui_text('report_summary_net', 'Net', 'Net') => formatAmountByType($sumIn - $sumOut, 'TRY', $supportedCurrencies),
            ],
        ];
    }

    if ($reportType === 'profit') {
        $title = ui_text('web_report_profit', 'Kar / Zarar', 'Profit / Loss');
        $headers = [
            ui_text('report_header_date', 'Tarih', 'Date'),
            ui_text('report_header_type', 'Tip', 'Type'),
            ui_text('report_header_currency', 'PB', 'Currency'),
            ui_text('report_header_debt', 'Borç', 'Debt'),
            ui_text('report_header_credit', 'Alacak', 'Credit'),
            ui_text('report_header_description', 'Açıklama', 'Description'),
        ];
        $rows = [];
        $sumDebt = 0.0;
        $sumCredit = 0.0;
        $st = $db->prepare(
            "SELECT event_date, event_type, currency, debt_original, credit_original, description
             FROM customer_history_events
             WHERE profile_id=:pid AND event_date BETWEEN :d1 AND :d2
             ORDER BY event_date DESC, created_at DESC"
        );
        $st->execute([':pid' => $profileId, ':d1' => ($reportStart . ' 00:00:00'), ':d2' => ($reportEnd . ' 23:59:59')]);
        foreach ($st->fetchAll() ?: [] as $r) {
            $cur = trim((string)($r['currency'] ?? 'TRY'));
            if ($cur === '') {
                $cur = 'TRY';
            }
            $rows[] = [
                trDate((string)$r['event_date']),
                (string)($r['event_type'] ?? ''),
                $cur,
                formatAmountByType((float)($r['debt_original'] ?? 0), $cur, $supportedCurrencies),
                formatAmountByType((float)($r['credit_original'] ?? 0), $cur, $supportedCurrencies),
                (string)($r['description'] ?? ''),
            ];
            $sumDebt += (float)($r['debt_original'] ?? 0);
            $sumCredit += (float)($r['credit_original'] ?? 0);
        }
        return [
            'title' => $title,
            'headers' => $headers,
            'rows' => $rows,
            'summary' => [
                ui_text('report_summary_total_debt', 'Toplam Borç', 'Total Debt') => formatAmountByType($sumDebt, 'TRY', $supportedCurrencies),
                ui_text('report_summary_total_credit', 'Toplam Alacak', 'Total Credit') => formatAmountByType($sumCredit, 'TRY', $supportedCurrencies),
                ui_text('report_summary_net_credit_debt', 'Net (Alacak-Borç)', 'Net (Credit-Debt)') => formatAmountByType($sumCredit - $sumDebt, 'TRY', $supportedCurrencies),
            ],
        ];
    }

    $stockTypeMap = [
        'stock' => '',
        'stock_product' => 'Product',
        'stock_scrap' => 'Scrap',
        'stock_ziynet' => 'Ziynet',
    ];
    if (array_key_exists($reportType, $stockTypeMap)) {
        $dbStockType = $stockTypeMap[$reportType];
        $title = ui_text('report_stock_title', 'Stok Raporu', 'Stock Report');
        if ($dbStockType === 'Product') {
            $title = ui_text('product_status_tab', 'Altın Stok', 'Gold Stock');
        } elseif ($dbStockType === 'Scrap') {
            $title = ui_text('scrap_status_tab', 'Hurda Stok', 'Scrap Stock');
        } elseif ($dbStockType === 'Ziynet') {
            $title = ui_text('ziynet_status_tab', 'Ziynet Stok', 'Ziynet Stock');
        }
        $rows = [];
        $sumQty = 0.0;
        $sumValue = 0.0;
        $params = [':pid' => $profileId];
        if ($depotName !== '') {
            $params[':dn'] = $depotName;
        }

        if ($dbStockType === 'Product') {
            $headers = ['Depo', 'Stok Tipi', 'Ürün', 'Miktar', 'Barkod', 'Gram', 'Ayar', 'Toplam Değer'];
            $headers = [
                ui_text('report_header_depot', 'Depo', 'Depot'),
                ui_text('report_header_stock_type', 'Stok Tipi', 'Stock Type'),
                ui_text('report_header_product', 'Ürün', 'Product'),
                ui_text('report_header_quantity', 'Miktar', 'Quantity'),
                ui_text('report_header_barcode', 'Barkod', 'Barcode'),
                ui_text('report_header_gram', 'Gram', 'Gram'),
                ui_text('report_header_karat', 'Ayar', 'Karat'),
                ui_text('report_header_total_value', 'Toplam Değer', 'Total Value'),
            ];
            $sql = "SELECT COALESCE(d.name,'Depo') depot_name, s.stock_type, s.item_type_name, s.quantity, s.unique_barcode, s.quantity_gr, s.purchase_price_base, p.karat_purity 
                    FROM stock_items s 
                    LEFT JOIN depots d ON d.id=s.depot_id AND d.profile_id=s.profile_id 
                    LEFT JOIN product_definitions p ON p.name=s.item_type_name AND p.profile_id=s.profile_id AND p.stock_type='Product'
                    WHERE s.profile_id=:pid AND s.is_deleted=0 AND s.stock_type='Product' AND s.quantity>0";
            if ($depotName !== '') {
                $sql .= " AND d.name=:dn";
            }
            $sql .= " ORDER BY d.name, s.item_type_name";

            $st = $db->prepare($sql);
            $st->execute($params);
            foreach ($st->fetchAll() ?: [] as $r) {
                $dn = trim((string)($r['depot_name'] ?? 'Depo'));
                if ($allowedDepotMap && !isset($allowedDepotMap[strtolower($dn)])) {
                    continue;
                }
                $qty = (float)($r['quantity'] ?? 0);
                $gram = (float)($r['quantity_gr'] ?? 0);
                $val = ($gram > 0 ? $gram : $qty) * (float)($r['purchase_price_base'] ?? 0);
                $rows[] = [
                    $dn,
                    ui_text('default_category_gold', 'Altın', 'Gold'),
                    (string)($r['item_type_name'] ?? ''),
                    formatAmountByType($qty, 'GRAM', $supportedCurrencies),
                    (string)($r['unique_barcode'] ?? '-'),
                    formatAmountByType($gram, 'GRAM', $supportedCurrencies),
                    (string)($r['karat_purity'] ?? '-'),
                    formatAmountByType($val, 'TRY', $supportedCurrencies),
                ];
                $sumQty += $qty;
                $sumValue += $val;
            }
        } else {
            $headers = [
                ui_text('report_header_depot', 'Depo', 'Depot'),
                ui_text('report_header_stock_type', 'Stok Tipi', 'Stock Type'),
                ui_text('report_header_product', 'Ürün', 'Product'),
                ui_text('report_header_quantity', 'Miktar', 'Quantity'),
                ui_text('report_header_total_value', 'Toplam Değer', 'Total Value'),
            ];
            $sql = "SELECT COALESCE(d.name,'Depo') depot_name, s.stock_type, s.item_type_name, SUM(s.quantity) total_qty, SUM(s.quantity * s.purchase_price_base) total_value
                    FROM stock_items s
                    LEFT JOIN depots d ON d.id=s.depot_id AND d.profile_id=s.profile_id
                    WHERE s.profile_id=:pid AND s.is_deleted=0";
            if ($dbStockType !== '') {
                $sql .= " AND s.stock_type=:st";
                $params[':st'] = $dbStockType;
            }
            if ($depotName !== '') {
                $sql .= " AND d.name=:dn";
            }
            $sql .= " GROUP BY d.name,s.stock_type,s.item_type_name ORDER BY d.name,s.stock_type,s.item_type_name";

            $st = $db->prepare($sql);
            $st->execute($params);
            foreach ($st->fetchAll() ?: [] as $r) {
                $dn = trim((string)($r['depot_name'] ?? 'Depo'));
                if ($allowedDepotMap && !isset($allowedDepotMap[strtolower($dn)])) {
                    continue;
                }
                $qty = (float)($r['total_qty'] ?? 0);
                $val = (float)($r['total_value'] ?? 0);
                $rows[] = [
                    $dn,
                    stockTypeLabel((string)($r['stock_type'] ?? 'Product')),
                    (string)($r['item_type_name'] ?? ''),
                    formatAmountByType($qty, 'GRAM', $supportedCurrencies),
                    formatAmountByType($val, 'TRY', $supportedCurrencies),
                ];
                $sumQty += $qty;
                $sumValue += $val;
            }
        }
        return [
            'title' => $title,
            'headers' => $headers,
            'rows' => $rows,
            'summary' => [
                ui_text('report_summary_total_items', 'Toplam Kalem', 'Total Items') => (string)count($rows),
                ui_text('report_summary_total_quantity', 'Toplam Miktar', 'Total Quantity') => formatAmountByType($sumQty, 'GRAM', $supportedCurrencies),
                ui_text('report_summary_total_value', 'Toplam Değer', 'Total Value') => formatAmountByType($sumValue, 'TRY', $supportedCurrencies),
            ],
        ];
    }

    if ($reportType === 'customer_statement') {
        $title = ui_text('web_report_customer_statement', 'Müşteri Ekstresi', 'Customer Statement');
        $headers = [
            ui_text('report_header_date', 'Tarih', 'Date'),
            ui_text('report_header_customer', 'Müşteri', 'Customer'),
            ui_text('report_header_type', 'Tip', 'Type'),
            ui_text('report_header_currency', 'PB', 'Currency'),
            ui_text('report_header_debt', 'Borç', 'Debt'),
            ui_text('report_header_credit', 'Alacak', 'Credit'),
            ui_text('report_header_description', 'Açıklama', 'Description'),
        ];
        $rows = [];
        $sumDebt = 0.0;
        $sumCredit = 0.0;
        $sql = "SELECT event_date, person_full_name, event_type, currency, debt_original, credit_original, description
                FROM customer_history_events
                WHERE profile_id=:pid AND event_date BETWEEN :d1 AND :d2";
        $params = [':pid' => $profileId, ':d1' => ($reportStart . ' 00:00:00'), ':d2' => ($reportEnd . ' 23:59:59')];
        if ($customerName !== '') {
            $sql .= " AND person_full_name=:pn";
            $params[':pn'] = $customerName;
        }
        $sql .= " ORDER BY event_date DESC, created_at DESC";
        $st = $db->prepare($sql);
        $st->execute($params);
        foreach ($st->fetchAll() ?: [] as $r) {
            $cur = trim((string)($r['currency'] ?? 'TRY'));
            if ($cur === '') {
                $cur = 'TRY';
            }
            $debt = (float)($r['debt_original'] ?? 0);
            $credit = (float)($r['credit_original'] ?? 0);
            $desc = trim((string)($r['description'] ?? ''));
            if (str_starts_with($desc, '{')) {
                $j = json_decode($desc, true);
                if (is_array($j) && !empty($j['main_desc'])) {
                    $desc = $j['main_desc'];
                }
            }
            $rows[] = [
                trDate((string)$r['event_date']),
                (string)($r['person_full_name'] ?? ''),
                (string)($r['event_type'] ?? ''),
                $cur,
                formatAmountByType($debt, $cur, $supportedCurrencies),
                formatAmountByType($credit, $cur, $supportedCurrencies),
                $desc, // JSON arındırılmış temiz açıklama
            ];
            $sumDebt += $debt;
            $sumCredit += $credit;
        }
        return [
            'title' => $title,
            'headers' => $headers,
            'rows' => $rows,
            'summary' => [
                ui_text('report_summary_total_debt', 'Toplam Borç', 'Total Debt') => formatAmountByType($sumDebt, 'TRY', $supportedCurrencies),
                ui_text('report_summary_total_credit', 'Toplam Alacak', 'Total Credit') => formatAmountByType($sumCredit, 'TRY', $supportedCurrencies),
                ui_text('report_summary_net', 'Net', 'Net') => formatAmountByType($sumCredit - $sumDebt, 'TRY', $supportedCurrencies),
            ],
        ];
    }

    if ($reportType === 'service_general') {
        $title = ui_text('web_report_service_general', 'Genel Hizmet', 'General Service');
        $headers = [
            ui_text('report_header_date', 'Tarih', 'Date'),
            ui_text('report_header_service_type', 'Hizmet Türü', 'Service Type'),
            ui_text('report_header_customer', 'Müşteri', 'Customer'),
            ui_text('report_header_status', 'Durum', 'Status'),
            ui_text('report_header_fee', 'Ücret', 'Fee'),
            ui_text('report_header_description', 'Açıklama', 'Description'),
        ];
        $rows = [];
        $sumFee = 0.0;
        $sql = "SELECT j.start_date, COALESCE(d.name,'-') service_name, j.customer_name, j.status, j.fee, j.currency, j.description
                FROM service_jobs j
                LEFT JOIN service_definitions d ON d.id=j.service_def_id AND d.profile_id=j.profile_id
                WHERE j.profile_id=:pid AND j.start_date BETWEEN :d1 AND :d2";
        $params = [':pid' => $profileId, ':d1' => $reportStart, ':d2' => $reportEnd];
        if ($customerName !== '') {
            $sql .= " AND j.customer_name LIKE :customer";
            $params[':customer'] = '%' . $customerName . '%';
        }
        $sql .= " ORDER BY j.start_date DESC, j.customer_name ASC";
        $st = $db->prepare($sql);
        $st->execute($params);
        foreach ($st->fetchAll() ?: [] as $r) {
            $fee = (float)($r['fee'] ?? 0);
            $cur = trim((string)($r['currency'] ?? 'TRY'));
            if ($cur === '') {
                $cur = 'TRY';
            }
            $status = (string)($r['status'] ?? '');
            $statusMap = [
                'Alindi' => ui_text('repair_status_received', 'Alındı', 'Received'),
                'Alındı' => ui_text('repair_status_received', 'Alındı', 'Received'),
                'Islemde' => ui_text('repair_status_in_progress', 'İşlemde', 'In Progress'),
                'İşlemde' => ui_text('repair_status_in_progress', 'İşlemde', 'In Progress'),
                'Hazir' => ui_text('repair_status_ready', 'Hazır', 'Ready'),
                'Hazır' => ui_text('repair_status_ready', 'Hazır', 'Ready'),
                'Teslim Edildi' => ui_text('repair_status_delivered', 'Teslim Edildi', 'Delivered'),
            ];
            $rows[] = [
                trDate((string)$r['start_date']),
                (string)($r['service_name'] ?? '-'),
                (string)($r['customer_name'] ?? ''),
                ($statusMap[$status] ?? $status),
                formatAmountByType($fee, $cur, $supportedCurrencies) . ' ' . $cur,
                (string)($r['description'] ?? ''),
            ];
            $sumFee += $fee;
        }
        return [
            'title' => $title,
            'headers' => $headers,
            'rows' => $rows,
            'summary' => [
                ui_text('report_summary_record_count', 'Kayıt Sayısı', 'Record Count') => (string)count($rows),
                ui_text('report_summary_total_fee', 'Toplam Ücret', 'Total Fee') => formatAmountByType($sumFee, 'TRY', $supportedCurrencies),
            ],
        ];
    }

    if ($reportType === 'tax') {
        $title = ui_text('web_report_tax', 'Vergi Raporu', 'Tax Report');
        $headers = [
            ui_text('report_header_date', 'Tarih', 'Date'),
            ui_text('report_header_source', 'Kaynak', 'Source'),
            ui_text('report_header_currency', 'PB', 'Currency'),
            ui_text('report_header_tax_base', 'Matrah', 'Tax Base'),
            ui_text('report_header_tax', 'Vergi', 'Tax'),
            ui_text('report_header_grand_total', 'Genel Toplam', 'Grand Total'),
        ];
        $rows = [];
        $sumBase = 0.0;
        $sumTax = 0.0;
        $sumGrand = 0.0;
        $st = $db->prepare(
            "SELECT created_at, source_table, currency, subtotal, tax_total, grand_total
             FROM tax_applications
             WHERE profile_id=:pid AND created_at BETWEEN :d1 AND :d2
             ORDER BY created_at DESC"
        );
        $st->execute([':pid' => $profileId, ':d1' => ($reportStart . ' 00:00:00'), ':d2' => ($reportEnd . ' 23:59:59')]);
        foreach ($st->fetchAll() ?: [] as $r) {
            $cur = trim((string)($r['currency'] ?? 'TRY'));
            if ($cur === '') {
                $cur = 'TRY';
            }
            $subtotal = (float)($r['subtotal'] ?? 0);
            $taxTotal = (float)($r['tax_total'] ?? 0);
            $grand = (float)($r['grand_total'] ?? 0);
            $rows[] = [
                trDate((string)$r['created_at']),
                (string)($r['source_table'] ?? ''),
                $cur,
                formatAmountByType($subtotal, $cur, $supportedCurrencies),
                formatAmountByType($taxTotal, $cur, $supportedCurrencies),
                formatAmountByType($grand, $cur, $supportedCurrencies),
            ];
            $sumBase += $subtotal;
            $sumTax += $taxTotal;
            $sumGrand += $grand;
        }
        return [
            'title' => $title,
            'headers' => $headers,
            'rows' => $rows,
            'summary' => [
                ui_text('report_summary_tax_base', 'Toplam Matrah', 'Total Tax Base') => formatAmountByType($sumBase, 'TRY', $supportedCurrencies),
                ui_text('report_summary_tax_total', 'Toplam Vergi', 'Total Tax') => formatAmountByType($sumTax, 'TRY', $supportedCurrencies),
                ui_text('report_summary_grand_total', 'Toplam Genel', 'Grand Total') => formatAmountByType($sumGrand, 'TRY', $supportedCurrencies),
            ],
        ];
    }

    return buildReportDataset(
        $db,
        $profileId,
        'cashflow',
        $reportStart,
        $reportEnd,
        $supportedCurrencies,
        $customerName,
        $depotName,
        $allowedDepotNames
    );
}
function loadComposerAutoload(): bool
{
    static $loaded = false;
    if ($loaded) {
        return true;
    }
    $paths = [
        __DIR__ . '/vendor/autoload.php',
        dirname(__DIR__) . '/vendor/autoload.php',
        dirname(__DIR__, 2) . '/vendor/autoload.php',
    ];
    foreach ($paths as $p) {
        if (is_file($p)) {
            require_once $p;
            $loaded = true;
            return true;
        }
    }
    return false;
}
function reportFileName(string $title, string $ext): string
{
    $base = preg_replace('/[^a-zA-Z0-9_-]+/', '_', strtolower($title));
    $base = trim((string)$base, '_');
    if ($base === '') {
        $base = 'rapor';
    }
    return $base . '_' . date('Ymd_His') . '.' . $ext;
}
function outputReportExcel(string $title, array $headers, array $rows, array $summary): void
{
    if (!class_exists('\\PhpOffice\\PhpSpreadsheet\\Spreadsheet') || !class_exists('\\PhpOffice\\PhpSpreadsheet\\Writer\\Xlsx')) {
        throw new RuntimeException('PhpSpreadsheet bulunamadi. Once composer ile kurulum yapin.');
    }
    $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
    $sheet = $spreadsheet->getActiveSheet();
    $sheet->setTitle('Rapor');
    $rowIdx = 1;
    $colCount = max(1, count($headers));
    for ($i = 0; $i < $colCount; $i++) {
        $col = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i + 1);
        $sheet->setCellValue($col . $rowIdx, (string)($headers[$i] ?? ''));
    }
    $rowIdx++;
    foreach ($rows as $r) {
        for ($i = 0; $i < $colCount; $i++) {
            $col = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i + 1);
            $sheet->setCellValue($col . $rowIdx, (string)($r[$i] ?? ''));
        }
        $rowIdx++;
    }
    if ($summary) {
        $rowIdx++;
        foreach ($summary as $k => $v) {
            $sheet->setCellValue('A' . $rowIdx, (string)$k);
            $sheet->setCellValue('B' . $rowIdx, (string)$v);
            $rowIdx++;
        }
    }
    for ($i = 0; $i < $colCount; $i++) {
        $col = \PhpOffice\PhpSpreadsheet\Cell\Coordinate::stringFromColumnIndex($i + 1);
        $sheet->getColumnDimension($col)->setAutoSize(true);
    }
    $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
    while (ob_get_level() > 0) {
        ob_end_clean();
    }
    header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    header('Content-Disposition: attachment; filename="' . reportFileName($title, 'xlsx') . '"');
    header('Cache-Control: max-age=0');
    $writer->save('php://output');
    exit;
}
function outputReportPdf(string $title, array $headers, array $rows, array $summary): void
{
    if (class_exists('\\Dompdf\\Dompdf')) {
        $html = '<html><head><meta charset="utf-8"><style>body{font-family:DejaVu Sans,sans-serif;font-size:11px;}table{width:100%;border-collapse:collapse;}th,td{border:1px solid #bbb;padding:4px;text-align:left;}h2{margin:0 0 10px;} .sum{margin-top:10px;}</style></head><body>';
        $html .= '<h2>' . e($title) . '</h2><table><thead><tr>';
        foreach ($headers as $h) {
            $html .= '<th>' . e((string)$h) . '</th>';
        }
        $html .= '</tr></thead><tbody>';
        foreach ($rows as $r) {
            $html .= '<tr>';
            foreach ($r as $c) {
                $html .= '<td>' . e((string)$c) . '</td>';
            }
            $html .= '</tr>';
        }
        if (!$rows) {
            $html .= '<tr><td colspan="' . count($headers) . '">' . e(ui_text('no_records', 'Kayıt yok.', 'No records.')) . '</td></tr>';
        }
        $html .= '</tbody></table>';
        if ($summary) {
            $html .= '<table class="sum"><tbody>';
            foreach ($summary as $k => $v) {
                $html .= '<tr><td>' . e((string)$k) . '</td><td>' . e((string)$v) . '</td></tr>';
            }
            $html .= '</tbody></table>';
        }
        $html .= '</body></html>';
        $dompdf = new \Dompdf\Dompdf(['isRemoteEnabled' => true]);
        $dompdf->loadHtml($html, 'UTF-8');
        $dompdf->setPaper('A4', 'landscape');
        $dompdf->render();
        while (ob_get_level() > 0) {
            ob_end_clean();
        }
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . reportFileName($title, 'pdf') . '"');
        echo $dompdf->output();
        exit;
    }
    if (class_exists('\\FPDF')) {
        $pdf = new \FPDF('L', 'mm', 'A4');
        $pdf->AddPage();
        $pdf->SetFont('Arial', 'B', 12);
        $pdf->Cell(0, 8, $title, 0, 1);
        $pdf->SetFont('Arial', 'B', 9);
        foreach ($headers as $h) {
            $pdf->Cell(45, 7, iconv('UTF-8', 'ISO-8859-9//TRANSLIT', (string)$h), 1, 0);
        }
        $pdf->Ln();
        $pdf->SetFont('Arial', '', 8);
        foreach ($rows as $r) {
            foreach ($r as $c) {
                $pdf->Cell(45, 6, iconv('UTF-8', 'ISO-8859-9//TRANSLIT', substr((string)$c, 0, 42)), 1, 0);
            }
            $pdf->Ln();
        }
        while (ob_get_level() > 0) {
            ob_end_clean();
        }
        header('Content-Type: application/pdf');
        header('Content-Disposition: attachment; filename="' . reportFileName($title, 'pdf') . '"');
        echo $pdf->Output('S');
        exit;
    }
    throw new RuntimeException('PDF kutuphanesi bulunamadi. Dompdf veya FPDF kurulumu gerekli.');
}

$error = '';
$tenantError = '';
$flash = pullFlash();
$act = trim((string)($_POST['action'] ?? ''));
$rememberCookieName = (string)$REMEMBER_ME_COOKIE;
$webHardwareCookieName = (string)$WEB_HARDWARE_COOKIE;
$webDeviceNameCookieName = (string)$WEB_DEVICE_NAME_COOKIE;

if (isset($_GET['logout']) || $act === 'logout_all') {
    try {
        $masterForLogout = golder_master_pdo();
        $webSessionUuid = trim((string)($_SESSION['web_session_uuid'] ?? ''));
        if ($webSessionUuid !== '') {
            $sessionRow = golder_get_web_session_by_uuid($masterForLogout, $webSessionUuid);
            $customerId = (int)($_SESSION['customer']['id'] ?? 0);
            $deviceId = (int)($sessionRow['device_id'] ?? 0);
            if ($customerId > 0 && $deviceId > 0) {
                golder_remove_device($masterForLogout, $customerId, $deviceId);
            } else {
                golder_close_web_session($masterForLogout, $webSessionUuid, 'logout');
            }
        }
    } catch (Throwable $_ignored) {
    }
    try {
        $master = pdoConnect($MASTER_DB['host'], (int)$MASTER_DB['port'], $MASTER_DB['name'], $MASTER_DB['user'], $MASTER_DB['pass']);
        rememberDeleteTokenFromCookie($master, $rememberCookieName);
    } catch (Throwable $_ignored) {
    }
    rememberClearCookie($rememberCookieName);
    setcookie($webHardwareCookieName, '', rememberCookieOptions(time() - 86400));
    unset($_COOKIE[$webHardwareCookieName]);
    clearSessionAll();
    go();
}
if ($act === 'logout_user') {
    unset($_SESSION['user']);
    flash('ok', 'Kullanıcı çıkışı yapıldı.');
    go();
}
if ($act === 'change_profile') {
    unset($_SESSION['profile'], $_SESSION['user']);
    flash('ok', 'Profil seçimine dönüldü.');
    go();
}

if (empty($_SESSION['customer']) && $act !== 'customer_login') {
    $rememberRow = rememberTryAutoCustomerLogin($MASTER_DB, $rememberCookieName);
    if (is_array($rememberRow) && $rememberRow) {
        $webHardwareId = substr(trim((string)($_COOKIE[$webHardwareCookieName] ?? '')), 0, 100);
        $webDeviceName = substr(trim((string)($_COOKIE[$webDeviceNameCookieName] ?? '')), 0, 100);
        $rememberPlan = golder_plan_label((string)($rememberRow['plan_type'] ?? ''));
        $rememberFailure = golder_customer_web_access_failure($rememberRow);
        if (is_array($rememberFailure)) {
            $rememberPlan .= ' / ' . (string)($rememberFailure['code'] ?? 'WEB_ACCESS_DISABLED');
            try {
                $m = pdoConnect($MASTER_DB['host'], (int)$MASTER_DB['port'], $MASTER_DB['name'], $MASTER_DB['user'], $MASTER_DB['pass']);
                rememberDeleteTokenFromCookie($m, $rememberCookieName);
            } catch (Throwable $_e) {
            }
            rememberClearCookie($rememberCookieName);
            flash('err', (string)($rememberFailure['message'] ?? ('Web erisimi kapali: ' . $rememberPlan)));
            go(['act' => 'customer_login']);
        }

        try {
            $webSession = golder_open_web_session(
                golder_master_pdo(),
                $rememberRow,
                ($webDeviceName !== '' ? $webDeviceName : ('WEB-' . substr(hash('sha256', golder_user_agent()), 0, 8))),
                golder_client_ip(),
                golder_user_agent(),
                24,
                $webHardwareId
            );
            if (empty($webSession['ok'])) {
                flash('err', (string)($webSession['message'] ?? 'Web session limit reached.'));
                go(['act' => 'customer_login']);
            }
        } catch (Throwable $e) {
            flash('err', 'Web session could not be started: ' . $e->getMessage());
            go(['act' => 'customer_login']);
        }

        session_regenerate_id();
        $_SESSION['customer'] = customerSessionPayload($rememberRow);
        $_SESSION['tenant'] = customerTenantPayload($rememberRow);
        $_SESSION['web_session_uuid'] = (string)($webSession['session_uuid'] ?? '');
        $_SESSION['web_session_token'] = (string)($webSession['session_token'] ?? '');
        try {
            rememberRotateToken(
                golder_master_pdo(),
                (string)($rememberRow['id'] ?? ''),
                $rememberCookieName,
                (int)$REMEMBER_ME_DAYS
            );
        } catch (Throwable $e) {
            error_log('[golder remember] auto-login token rotate failed: ' . $e->getMessage());
        }
        unset($_SESSION['profile'], $_SESSION['user']);
        flash('ok', 'Beni Hatirla ile otomatik giris yapildi.');
        go();
    }
}

// --- YENİ ŞİFRESİZ QR EŞLEŞTİRME (PAIRING) SİSTEMİ ---
if (empty($_SESSION['customer']) && $act === 'device_pair') {
    $token = trim((string)($_POST['pairing_token'] ?? ''));
    $pin = trim((string)($_POST['pin_code'] ?? ''));
    $hardwareId = trim((string)($_POST['hardware_id'] ?? ''));
    $deviceName = trim((string)($_POST['device_name'] ?? 'Web Cihazı'));

    if ($hardwareId === '') {
        $error = 'Cihaz kimliği (Hardware ID) okunamadı. Tarayıcı çerezleri engelliyor olabilir.';
    } else {
        try {
            $m = pdoConnect($MASTER_DB['host'], (int)$MASTER_DB['port'], $MASTER_DB['name'], $MASTER_DB['user'], $MASTER_DB['pass']);
            golder_ensure_pairing_schema($m);
            
            // QR Kod veya Manuel Pin ile arama yap
            if ($token !== '') {
                $st = $m->prepare("SELECT * FROM device_pairing_requests WHERE pairing_token = :t LIMIT 1");
                $st->execute([':t' => $token]);
            } elseif ($pin !== '') {
                $st = $m->prepare("SELECT * FROM device_pairing_requests WHERE pin_code = :p AND status = 'pending' LIMIT 1");
                $st->execute([':p' => $pin]);
            } else {
                $st = false;
            }

            $reqRow = $st ? $st->fetch() : null;

            if (!$reqRow) {
                $error = 'Girdiğiniz kod geçersiz veya bulunamadı.';
            } elseif ($reqRow['status'] !== 'pending' || strtotime($reqRow['expires_at']) < time()) {
                $error = 'Bu kodun süresi dolmuş. Lütfen bilgisayardan yeni bir cihaz ekleme kodu oluşturun.';
            } else {
                // Kod doğru! Müşteriyi bul ve içeri al.
                $customerId = (int)$reqRow['customer_id'];
                $stCust = $m->prepare("SELECT * FROM customers WHERE id = :id LIMIT 1");
                $stCust->execute([':id' => $customerId]);
                $row = $stCust->fetch();

                if (!$row) {
                    $error = 'Eşleşen müşteri hesabı bulunamadı.';
                } else {
                    $row = golder_enforce_customer_plan($m, $customerId) ?? $row;
                    $webSession = golder_open_web_session(
                        $m,
                        $row,
                        $deviceName,
                        golder_client_ip(),
                        golder_user_agent(),
                        24 * 365 // 1 Yıl boyunca açık kalabilir
                        , $hardwareId
                    );

                    if (empty($webSession['ok'])) {
                        $error = (string)($webSession['message'] ?? 'Web oturumu oluşturulamadı.');
                    } else {
                        // Kodu kullanıldı olarak işaretle
                        $m->prepare("UPDATE device_pairing_requests SET status = 'paired' WHERE id = :id")->execute([':id' => $reqRow['id']]);

                        session_regenerate_id();
                        $_SESSION['customer'] = customerSessionPayload($row);
                        $_SESSION['tenant'] = customerTenantPayload($row);
                        $_SESSION['web_session_uuid'] = (string)($webSession['session_uuid'] ?? '');
                        $_SESSION['web_session_token'] = (string)($webSession['session_token'] ?? '');
                        setcookie(
                            $webHardwareCookieName,
                            substr($hardwareId, 0, 100),
                            rememberCookieOptions(time() + ((int)$WEB_HARDWARE_COOKIE_DAYS * 86400))
                        );
                        $_COOKIE[$webHardwareCookieName] = substr($hardwareId, 0, 100);
                        if ($deviceName !== '') {
                            setcookie(
                                $webDeviceNameCookieName,
                                substr($deviceName, 0, 100),
                                rememberCookieOptions(time() + ((int)$WEB_DEVICE_NAME_COOKIE_DAYS * 86400))
                            );
                            $_COOKIE[$webDeviceNameCookieName] = substr($deviceName, 0, 100);
                        }
                        try {
                            rememberRotateToken(
                                $m,
                                (string)($row['id'] ?? ''),
                                $rememberCookieName,
                                (int)$REMEMBER_ME_DAYS
                            );
                        } catch (Throwable $e) {
                            error_log('[golder remember] pairing token issue failed: ' . $e->getMessage());
                        }
                        
                        unset($_SESSION['profile'], $_SESSION['user']);
                        flash('ok', 'Cihaz başarıyla eşleştirildi ve giriş yapıldı.');
                        go();
                    }
                }
            }
        } catch (Throwable $ex) {
            $error = 'Eşleştirme hatası: ' . $ex->getMessage();
        }
    }
}
// --------------------------------------------------------


$tenant = null;
if (!empty($_SESSION['customer'])) {
    try {
        $currentWebSession = golder_validate_current_web_customer_session(golder_master_pdo());
        
        if (!$currentWebSession) {
            // Oturum zaman aşımına uğramış (Telefon uyku moduna geçmiş ve cron kapatmış)
            // SADECE aktif PHP oturumunu temizliyoruz. Cihaz eşleştirme COOKIE'sini SİLMİYORUZ!
            golder_clear_web_app_customer_session();
            
            // Ana sayfaya yenileyerek yönlendir. 
            // Sayfa açılırken rememberTryAutoCustomerLogin() devreye girip sessizce yeni session açacak.
            go(); 
        }

        $masterForCustomer = golder_master_pdo();
        $currentCustomerId = (int)($_SESSION['customer']['id'] ?? 0);
        $currentCustomer = golder_enforce_customer_plan($masterForCustomer, $currentCustomerId);
        if (!is_array($currentCustomer)) {
            golder_clear_web_app_customer_session();
            go(['act' => 'customer_login']);
        }
        $webAccessFailure = golder_customer_web_access_failure($currentCustomer);
        if (is_array($webAccessFailure)) {
            $webSessionUuid = trim((string)($_SESSION['web_session_uuid'] ?? ''));
            if ($webSessionUuid !== '') {
                golder_close_web_session($masterForCustomer, $webSessionUuid, 'license_restricted');
            }
            try {
                rememberDeleteTokenFromCookie($masterForCustomer, $rememberCookieName);
            } catch (Throwable $_ignored) {
            }
            rememberClearCookie($rememberCookieName);
            golder_clear_web_app_customer_session();
            flash('err', (string)($webAccessFailure['message'] ?? 'Web arayuzu erisimi kapali.'));
            go(['act' => 'customer_login']);
        }
        $_SESSION['customer'] = customerSessionPayload($currentCustomer);
        $_SESSION['tenant'] = customerTenantPayload($currentCustomer);

        $webSessionUuid = trim((string)($_SESSION['web_session_uuid'] ?? ''));
        if ($webSessionUuid !== '') {
            golder_touch_web_session(
                $masterForCustomer,
                $webSessionUuid,
                '',
                golder_client_ip(),
                golder_user_agent(),
                false
            );
        }
    } catch (Throwable $ex) {
        error_log('[golder web-session] ' . $ex->getMessage());
        // Hata durumunda da cookie'yi silmiyoruz, sadece session'ı düşürüyoruz.
        golder_clear_web_app_customer_session();
        go();
    }
}
if (!empty($_SESSION['customer'])) {
    try {
        $tenant = getTenantPdo($MASTER_DB);
        syncEnsureGlobalEventsTable($tenant);
    } catch (Throwable $ex) {
        $tenantError = 'Tenant bağlantı hatası: ' . $ex->getMessage();
    }
}

if ($tenant instanceof PDO && !empty($_SESSION['profile']['id'])) {
    try {
        $sessionProfile = profileRowById($tenant, (string)$_SESSION['profile']['id']);
        if (!$sessionProfile) {
            unset($_SESSION['profile'], $_SESSION['user']);
            flash('err', 'Secili profil artik mevcut degil. Lutfen tekrar profil secin.');
            go();
        }
        $_SESSION['profile']['id'] = (string)($sessionProfile['id'] ?? '');
        $_SESSION['profile']['name'] = (string)($sessionProfile['name'] ?? ($_SESSION['profile']['name'] ?? ''));
    } catch (Throwable $ex) {
        $tenantError = 'Profil dogrulanamadi: ' . $ex->getMessage();
    }
}

if ($tenant instanceof PDO && empty($_SESSION['profile'])) {
    if ($act === 'create_profile') {
        $name = trim((string)($_POST['profile_name'] ?? ''));
        if ($name === '') {
            flash('err', 'Profil adı zorunlu.');
            go();
        }
        try {
            $ex = $tenant->prepare("SELECT id FROM profiles WHERE name=:n LIMIT 1");
            $ex->execute([':n' => $name]);
            if ($ex->fetch()) {
                flash('err', 'Bu profil adı mevcut.');
                go();
            }
            $pid = uuid4();
            $adminId = uuid4();
            $profileSyncTx = uuid4();
            $tenant->beginTransaction();
            $tenant->prepare("INSERT INTO profiles (id,name,is_active,is_deleted,created_at) VALUES (:id,:n,1,0,NOW())")->execute([':id' => $pid, ':n' => $name]);
            settingUpsert($tenant, $pid, 'password_enabled', '0', 'bool');
            settingUpsert($tenant, $pid, 'password_hash', '', 'str');
            settingUpsert($tenant, $pid, 'language', web_lang_code(), 'str');
            $tenant->prepare("INSERT INTO users (id, profile_id, username, full_name, password_hash, role, is_active, is_deleted) VALUES (:id,:pid,'admin','Yönetici','', 'admin',1,0)")
                ->execute([':id' => $adminId, ':pid' => $pid]);
            $adminAfterRow = syncFetchRowSnapshot($tenant, 'users', $pid, $adminId);
            syncEnqueueGlobalEvent(
                $tenant,
                $pid,
                'users',
                'ADD',
                $adminId,
                null,
                $adminAfterRow,
                ['source' => 'web.create_profile'],
                $profileSyncTx,
                'WEB'
            );
            $profileAfterRow = syncFetchRowSnapshot($tenant, 'profiles', '', $pid);
            if (!is_array($profileAfterRow) || !$profileAfterRow) {
                $profileAfterRow = [
                    'id' => $pid,
                    'name' => $name,
                    'is_active' => 1,
                    'is_deleted' => 0,
                ];
            }
            // Global event: all desktop clients should receive new profile metadata.
            syncEnqueueGlobalEvent(
                $tenant,
                $pid,
                'profiles',
                'ADD',
                $pid,
                null,
                $profileAfterRow,
                ['source' => 'web.create_profile'],
                $profileSyncTx,
                'WEB'
            );
            $tenant->commit();
            flash('ok', 'Profil oluşturuldu: ' . $name);
            go();
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Profil oluşturulamadı: ' . $ex->getMessage());
            go();
        }
    }
    if ($act === 'select_profile') {
        $pid = trim((string)($_POST['profile_id'] ?? ''));
        if ($pid === '') {
            flash('err', 'Profil seçin.');
            go();
        }
        try {
            $st = $tenant->prepare("SELECT id,name FROM profiles WHERE id=:id AND is_active=1 AND is_deleted=0 LIMIT 1");
            $st->execute([':id' => $pid]);
            $row = $st->fetch();
            if (!$row) {
                flash('err', 'Profil bulunamadı.');
                go();
            }
            $_SESSION['profile'] = ['id' => (string)$row['id'], 'name' => (string)$row['name']];
            unset($_SESSION['user']);
            flash('ok', 'Profil seçildi: ' . (string)$row['name']);
            go();
        } catch (Throwable $ex) {
            flash('err', 'Profil seçimi hatası: ' . $ex->getMessage());
            go();
        }
    }
}

if ($tenant instanceof PDO && !empty($_SESSION['profile']) && empty($_SESSION['user']) && $act === 'user_login') {
    $profileId = (string)$_SESSION['profile']['id'];
    $username = trim((string)($_POST['profile_username'] ?? ''));
    $password = (string)($_POST['profile_password'] ?? '');
    if ($username === '') {
        flash('err', 'Kullanıcı zorunlu.');
        go();
    }
    try {
        ensureDefaultAdmin($tenant, $profileId);
        $st = $tenant->prepare("SELECT id,username,full_name,role FROM users WHERE profile_id=:pid AND username=:u AND is_active=1 AND is_deleted=0 LIMIT 1");
        $st->execute([':pid' => $profileId, ':u' => $username]);
        $row = $st->fetch();
        if (!$row) {
            flash('err', 'Kullanıcı bulunamadı.');
            go();
        }
        $uid = (string)$row['id'];
        $role = (string)$row['role'];
        $isVerified = false;

        // Kullan?c?n?n veritaban?ndaki ?ifre ?zetini al
        $stPass = $tenant->prepare("SELECT password_hash FROM users WHERE id=:id LIMIT 1");
        $stPass->execute([':id' => $uid]);
        $userHash = trim((string)$stPass->fetchColumn());

        // Profilin ?ifre durumunu ve personel durumunu kontrol et
        [$profilePassEnabled, $profilePassHash] = profilePassState($tenant, $profileId);
        $hasProfilePass = $profilePassEnabled && $profilePassHash !== '';
        $hasStaff = hasActiveNonAdminUsers($tenant, $profileId);
        $activeUserCount = countActiveUsers($tenant, $profileId);

        if ($password === '') {
            if ($role === 'admin' && !$hasProfilePass && !$hasStaff && $activeUserCount === 1 && $userHash === '') {
                $isVerified = true;
            }
        } else {
            // ??FRE G?R?LM??
            // 1. ?nce kullan?c?n?n KEND? ?ifresiyle e?le?iyor mu bak
            $isVerified = verifyUser($tenant, $uid, $profileId, $password);

            // Eski MD5 kontrol? (B?y?k/K???k harf duyarl?l???n? ??zerek tam g?venlik sa?lar)
            if (!$isVerified && $userHash !== '' && strlen($userHash) === 32 && strpos($userHash, '$') === false) {
                if (hash_equals(strtolower($userHash), strtolower(md5($password)))) {
                    $isVerified = true;
                    // G?venli PBKDF2'ye y?kselt
                    $tenant->prepare("UPDATE users SET password_hash=:h WHERE id=:id")->execute([':h' => pbkdfHash($password), ':id' => $uid]);
                }
            }

            // 2. E?er kendi ?ifresiyle G?REMED?YSE ve Admin ise, PROF?L ?ifresini dene (Python'un Fallback mant???)
            if (!$isVerified && $role === 'admin' && $hasProfilePass) {
                if (verifyProfilePassword($tenant, $profileId, $password)) {
                    $isVerified = true;
                    // Kullan?c?n?n ?ifresini profil ?ifresiyle e?itle (Senkronizasyon)
                    $tenant->prepare("UPDATE users SET password_hash=:h WHERE id=:id")->execute([':h' => pbkdfHash($password), ':id' => $uid]);
                }
            }
        }
        if (!$isVerified) {
            flash('err', 'Şifre hatalı. Lütfen tekrar deneyin.');
            go();
        }
        $perms = [];
        $allowDepot = [];
        $allowCash = [];
        $allowCard = [];
        if ($role !== 'admin') {
            $pst = $tenant->prepare("SELECT permission_code FROM user_permissions WHERE user_id=:uid");
            $pst->execute([':uid' => $uid]);
            foreach ($pst->fetchAll() as $pr) {
                $c = trim((string)($pr['permission_code'] ?? ''));
                if ($c !== '') {
                    $perms[] = $c;
                }
            }
            $sst = $tenant->prepare("SELECT scope_type,target_id FROM user_access_scopes WHERE user_id=:uid");
            $sst->execute([':uid' => $uid]);
            foreach ($sst->fetchAll() as $sr) {
                $scope = (string)($sr['scope_type'] ?? '');
                $target = (string)($sr['target_id'] ?? '');
                if ($target === '') {
                    continue;
                }
                if ($scope === 'depot') {
                    $allowDepot[] = $target;
                } elseif ($scope === 'cash_account') {
                    $allowCash[] = $target;
                } elseif ($scope === 'card_account') {
                    $allowCard[] = $target;
                }
            }
        }
        $_SESSION['user'] = [
            'id' => $uid,
            'username' => (string)$row['username'],
            'full_name' => (string)($row['full_name'] ?? $row['username']),
            'role' => $role,
            'permissions' => ($role === 'admin') ? ['ALL'] : $perms,
            'allowed_depots' => ($role === 'admin') ? null : $allowDepot,
            'allowed_cash_accounts' => ($role === 'admin') ? null : $allowCash,
            'allowed_card_accounts' => ($role === 'admin') ? null : $allowCard,
        ];
        flash('ok', 'Profil girişi başarılı.');
        go();
    } catch (Throwable $ex) {
        flash('err', 'Profil kullanıcı girişi hatası: ' . $ex->getMessage());
        go();
    }
}

if ($tenant instanceof PDO && !empty($_SESSION['profile']) && !empty($_SESSION['user'])) {
    $profileId = (string)$_SESSION['profile']['id'];
    $user = $_SESSION['user'];
    $isAdmin = (($user['role'] ?? '') === 'admin');
    $stockDefinitionPerms = ['perm_def_product', 'perm_def_scrap', 'perm_def_ziynet'];
    ensureStage5Tables($tenant);

    if ($act === 'add_stock_definition') {
        $name = trim((string)($_POST['definition_name'] ?? ''));
        $stockType = normalizeStockType((string)($_POST['definition_stock_type'] ?? 'Product'));
        $defaultPrice = parseAmount((string)($_POST['definition_default_purchase_price'] ?? ($_POST['default_price'] ?? '0')));
        $purchaseCurrency = strtoupper(trim((string)($_POST['definition_purchase_currency'] ?? ($_POST['purchase_currency'] ?? 'TRY'))));
        $karatPurity = trim((string)($_POST['definition_karat_purity'] ?? ($_POST['karat_purity'] ?? '')));
        $defaultWeight = parseAmount((string)($_POST['definition_default_weight_gr'] ?? ($_POST['default_weight'] ?? '0')));
        $categoryId = trim((string)($_POST['definition_category_id'] ?? ''));
        if ($name === '') {
            flash('err', 'Tanım adı zorunludur.');
            go(routeFromReq());
        }
        if (!in_array($stockType, $SUPPORTED_STOCK_TYPES, true)) {
            flash('err', 'Stok tipi geçersiz.');
            go(routeFromReq());
        }
        $requiredPerm = stockDefinitionPermissionForType($stockType);
        if (!hasPerm($user, $requiredPerm)) {
            flash('err', 'Bu stok tanimini yonetme yetkiniz yok.');
            go(routeFromReq());
        }
        if (!in_array($purchaseCurrency, $SUPPORTED_CURRENCIES, true)) {
            $purchaseCurrency = 'TRY';
        }
        try {
            $st = $tenant->prepare("SELECT id FROM product_definitions WHERE profile_id=:pid AND stock_type=:st AND name=:n AND is_deleted=0 LIMIT 1");
            $st->execute([':pid' => $profileId, ':st' => $stockType, ':n' => $name]);
            if ($st->fetch()) {
                flash('err', 'Bu tanım zaten mevcut.');
                go(routeFromReq());
            }
            $newDefinitionId = uuid4();
            $tenant->prepare("INSERT INTO product_definitions (id,profile_id,category_id,stock_type,name,barcode_header,karat_purity,workmanship_fee,workmanship_currency,default_purchase_price,default_sale_price,purchase_currency,sale_currency,default_weight_gr,is_active,is_deleted,created_at,updated_at) VALUES (:id,:pid,:cid,:st,:n,'',:kp,0,'TRY',:pp,0,:pc,:pc_sale,:dw,1,0,NOW(),NOW())")
                ->execute([
                    ':id' => $newDefinitionId,
                    ':pid' => $profileId,
                    ':cid' => ($categoryId !== '' ? $categoryId : null),
                    ':st' => $stockType,
                    ':n' => $name,
                    ':kp' => $karatPurity,
                    ':pp' => $defaultPrice,
                    ':pc' => $purchaseCurrency,
                    ':pc_sale' => $purchaseCurrency,
                    ':dw' => $defaultWeight,
                ]);
            $afterRow = syncFetchRowSnapshot($tenant, 'product_definitions', $profileId, $newDefinitionId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'product_definitions',
                'ADD',
                $newDefinitionId,
                null,
                $afterRow,
                ['source' => 'web.add_stock_definition']
            );
            flash('ok', 'Stok tanımı eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Stok tanımı eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'delete_stock_definition') {
        $definitionId = trim((string)($_POST['definition_id'] ?? ''));
        if ($definitionId === '') {
            flash('err', 'Silinecek tanım seçilmedi.');
            go(routeFromReq());
        }
        try {
            $stType = $tenant->prepare("SELECT stock_type FROM product_definitions WHERE id=:id AND profile_id=:pid AND is_deleted=0 LIMIT 1");
            $stType->execute([':id' => $definitionId, ':pid' => $profileId]);
            $defRow = $stType->fetch();
            if (!$defRow) {
                throw new RuntimeException('Stok tanimi bulunamadi.');
            }
            $requiredPerm = stockDefinitionPermissionForType((string)($defRow['stock_type'] ?? 'Product'));
            if (!hasPerm($user, $requiredPerm)) {
                flash('err', 'Bu stok tanimini silme yetkiniz yok.');
                go(routeFromReq());
            }
            $beforeRow = syncFetchRowSnapshot($tenant, 'product_definitions', $profileId, $definitionId);
            $tenant->prepare("UPDATE product_definitions SET is_deleted=1,updated_at=NOW() WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $definitionId, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'product_definitions', $profileId, $definitionId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'product_definitions',
                'UPDATE',
                $definitionId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.delete_stock_definition']
            );
            flash('ok', 'Stok tanımı silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Stok tanımı silinemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'add_depot') {
        if (!hasPerm($user, 'perm_depots')) {
            flash('err', 'Depo yonetimi icin yetkiniz yok.');
            go(routeFromReq());
        }
        $depotName = trim((string)($_POST['depot_name'] ?? ''));
        if ($depotName === '') {
            flash('err', 'Depo adı zorunludur.');
            go(routeFromReq());
        }
        try {
            $st = $tenant->prepare("SELECT id FROM depots WHERE profile_id=:pid AND name=:n AND is_deleted=0 LIMIT 1");
            $st->execute([':pid' => $profileId, ':n' => $depotName]);
            if ($st->fetch()) {
                flash('err', 'Bu depo zaten mevcut.');
                go(routeFromReq());
            }
            $newDepotId = uuid4();
            $tenant->prepare("INSERT INTO depots (id,profile_id,name,is_deleted,created_at,updated_at) VALUES (:id,:pid,:n,0,NOW(),NOW())")
                ->execute([':id' => $newDepotId, ':pid' => $profileId, ':n' => $depotName]);
            $afterRow = syncFetchRowSnapshot($tenant, 'depots', $profileId, $newDepotId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'depots',
                'ADD',
                $newDepotId,
                null,
                $afterRow,
                ['source' => 'web.add_depot']
            );
            flash('ok', 'Depo eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Depo eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'delete_depot') {
        if (!hasPerm($user, 'perm_depots')) {
            flash('err', 'Depo yonetimi icin yetkiniz yok.');
            go(routeFromReq());
        }
        $depotId = trim((string)($_POST['depot_id'] ?? ''));
        if ($depotId === '') {
            flash('err', 'Silinecek depo seçilmedi.');
            go(routeFromReq());
        }
        try {
            $st = $tenant->prepare("SELECT id FROM stock_items WHERE profile_id=:pid AND depot_id=:did AND is_deleted=0 AND quantity>0 LIMIT 1");
            $st->execute([':pid' => $profileId, ':did' => $depotId]);
            if ($st->fetch()) {
                flash('err', 'Bu depoda stok olduğu için silinemez.');
                go(routeFromReq());
            }
            $beforeRow = syncFetchRowSnapshot($tenant, 'depots', $profileId, $depotId);
            $tenant->prepare("UPDATE depots SET is_deleted=1,updated_at=NOW() WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $depotId, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'depots', $profileId, $depotId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'depots',
                'UPDATE',
                $depotId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.delete_depot']
            );
            flash('ok', 'Depo silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Depo silinemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'stock_transfer') {
        if (!hasPerm($user, 'perm_depots')) {
            flash('err', 'Depo transferi icin yetkiniz yok.');
            go(routeFromReq());
        }
        $sourceDepotId = trim((string)($_POST['source_depot_id'] ?? ''));
        $destDepotId = trim((string)($_POST['dest_depot_id'] ?? ''));
        $stockType = normalizeStockType((string)($_POST['transfer_stock_type'] ?? 'Product'));
        $itemName = trim((string)($_POST['transfer_item_name'] ?? ''));
        $qty = parseAmount((string)($_POST['transfer_quantity'] ?? '0'));
        if ($sourceDepotId === '' || $destDepotId === '' || $sourceDepotId === $destDepotId || $itemName === '' || $qty <= 0) {
            flash('err', 'Transfer verisi gecersiz.');
            go(routeFromReq());
        }
        try {
            $depSt = $tenant->prepare("SELECT id,name FROM depots WHERE profile_id=:pid AND (id=:src OR id=:dst) AND is_deleted=0");
            $depSt->execute([':pid' => $profileId, ':src' => $sourceDepotId, ':dst' => $destDepotId]);
            $depRows = $depSt->fetchAll() ?: [];
            $depMap = [];
            foreach ($depRows as $row) {
                $depMap[(string)($row['id'] ?? '')] = (string)($row['name'] ?? '');
            }
            $sourceDepot = trim((string)($depMap[$sourceDepotId] ?? ''));
            $destDepot = trim((string)($depMap[$destDepotId] ?? ''));
            if ($sourceDepot === '' || $destDepot === '') {
                throw new RuntimeException('Depo bulunamadi.');
            }
            if (!canAccessDepot($user, $sourceDepotId) || !canAccessDepot($user, $destDepotId)) {
                throw new RuntimeException('Depo erisim yetkiniz yok.');
            }
            $priceSt = $tenant->prepare(
                "SELECT COALESCE(AVG(purchase_price_base),0) p
                 FROM stock_items
                 WHERE profile_id=:pid AND depot_id=:did AND stock_type=:st
                   AND LOWER(TRIM(item_type_name))=LOWER(TRIM(:n))
                   AND is_deleted=0 AND quantity>0"
            );
            $priceSt->execute([':pid' => $profileId, ':did' => $sourceDepotId, ':st' => $stockType, ':n' => $itemName]);
            $sourceAvgPrice = (float)($priceSt->fetchColumn() ?? 0);
            $tenant->beginTransaction();
            stockRemoveByType($tenant, $profileId, $sourceDepot, $itemName, $stockType, $qty);
            stockAddByType($tenant, $profileId, $destDepot, $itemName, $stockType, $qty, $sourceAvgPrice);

            $moveId = uuid4();
            $tenant->prepare(
                "INSERT INTO stock_movements (id,profile_id,date,item_type,product_name,operation_type,quantity,source,destination,description,user,created_at)
                 VALUES (:id,:pid,NOW(),:it,:pn,'Transfer',:q,:src,:dst,:d,:u,NOW())"
            )->execute([
                ':id' => $moveId,
                ':pid' => $profileId,
                ':it' => $stockType,
                ':pn' => $itemName,
                ':q' => $qty,
                ':src' => $sourceDepot,
                ':dst' => $destDepot,
                ':d' => 'Depo transferi',
                ':u' => (string)($user['username'] ?? 'web'),
            ]);
            $moveAfter = syncFetchRowSnapshot($tenant, 'stock_movements', $profileId, $moveId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'stock_movements',
                'ADD',
                $moveId,
                null,
                $moveAfter,
                ['source' => 'web.stock_transfer']
            );
            $tenant->commit();
            flash('ok', 'Depo transferi tamamlandi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Depo transferi hatasi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'add_category') {
        if (!hasAnyPerm($user, $stockDefinitionPerms)) {
            flash('err', 'Kategori yonetimi icin yetkiniz yok.');
            go(routeFromReq());
        }
        $categoryName = trim((string)($_POST['category_name'] ?? ''));
        if ($categoryName === '') {
            flash('err', 'Kategori adi zorunludur.');
            go(routeFromReq());
        }
        try {
            $st = $tenant->prepare("SELECT id FROM categories WHERE profile_id=:pid AND name=:n LIMIT 1");
            $st->execute([':pid' => $profileId, ':n' => $categoryName]);
            if ($st->fetch()) {
                flash('err', 'Bu kategori zaten var.');
                go(routeFromReq());
            }
            $newCategoryId = uuid4();
            $tenant->prepare("INSERT INTO categories (id,profile_id,name) VALUES (:id,:pid,:n)")
                ->execute([':id' => $newCategoryId, ':pid' => $profileId, ':n' => $categoryName]);
            $afterRow = syncFetchRowSnapshot($tenant, 'categories', $profileId, $newCategoryId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'categories',
                'ADD',
                $newCategoryId,
                null,
                $afterRow,
                ['source' => 'web.add_category']
            );
            flash('ok', 'Kategori eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Kategori eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'delete_category') {
        if (!hasAnyPerm($user, $stockDefinitionPerms)) {
            flash('err', 'Kategori yonetimi icin yetkiniz yok.');
            go(routeFromReq());
        }
        $categoryId = trim((string)($_POST['category_id'] ?? ''));
        if ($categoryId === '') {
            flash('err', 'Silinecek kategori secilmedi.');
            go(routeFromReq());
        }
        try {
            // Silinmeden önce adını kontrol et
            $catSt = $tenant->prepare("SELECT name FROM categories WHERE id=:id AND profile_id=:pid LIMIT 1");
            $catSt->execute([':id' => $categoryId, ':pid' => $profileId]);
            $catRow = $catSt->fetch();
            if ($catRow) {
                $catNameCheck = str_replace(['ı','İ','ğ','Ğ','ü','Ü','ş','Ş','ö','Ö','ç','Ç'], ['i','i','g','g','u','u','s','s','o','o','c','c'], mb_strtolower(trim((string)$catRow['name']), 'UTF-8'));
                if (in_array($catNameCheck, ['altin', 'gumus'], true)) {
                    flash('err', 'Sistem için gerekli olan Altın veya Gümüş kategorileri silinemez.');
                    go(routeFromReq());
                }
            }
            $beforeRow = syncFetchRowSnapshot($tenant, 'categories', $profileId, $categoryId);
            $tenant->prepare("DELETE FROM categories WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $categoryId, ':pid' => $profileId]);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'categories',
                'DELETE',
                $categoryId,
                $beforeRow,
                null,
                ['source' => 'web.delete_category']
            );
            flash('ok', 'Kategori silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Kategori silinemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'add_financial_account') {
        if (!hasAnyPerm($user, ['perm_cash_view', 'perm_card_view'])) {
            flash('err', 'Bu i?lem i?in yetkiniz yok.');
            go(routeFromReq());
        }
        $name = trim((string)($_POST['account_name'] ?? ''));
        $type = trim((string)($_POST['account_type'] ?? ''));
        if ($name === '' || !in_array($type, ['cash', 'card'], true)) {
            flash('err', 'Hesap ad? ve t?r? zorunlu.');
            go(routeFromReq());
        }
        try {
            $st = $tenant->prepare("SELECT id FROM financial_accounts WHERE profile_id=:pid AND type=:t AND name=:n AND is_deleted=0 LIMIT 1");
            $st->execute([':pid' => $profileId, ':t' => $type, ':n' => $name]);
            if ($st->fetch()) {
                flash('err', 'Bu hesap zaten mevcut.');
                go(routeFromReq());
            }
            
            $newAccountId = uuid4();
            $tenant->prepare("INSERT INTO financial_accounts (id,profile_id,name,type,is_deleted,created_at,updated_at) VALUES (:id,:pid,:n,:t,0,NOW(),NOW())")
                ->execute([':id' => $newAccountId, ':pid' => $profileId, ':n' => $name, ':t' => $type]);
                
            // G?NCELLEME: Senkronizasyon (Sync) kuyru?una at ki bilgisayara gitsin!
            $afterRow = syncFetchRowSnapshot($tenant, 'financial_accounts', $profileId, $newAccountId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'financial_accounts',
                'ADD',
                $newAccountId,
                null,
                $afterRow,
                ['source' => 'web.add_financial_account']
            );

            flash('ok', 'Hesap eklendi ve masa?st?ne senkronize edildi.');
        } catch (Throwable $ex) {
            flash('err', 'Hesap eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'add_cash_flow') {
        $type = trim((string)($_POST['transaction_type'] ?? ''));
        if (!in_array($type, ['Income', 'Expense'], true)) {
            flash('err', 'Geçersiz işlem tipi.');
            go(routeFromReq());
        }
        if ($type === 'Income' && !hasPerm($user, 'perm_add_income')) {
            flash('err', 'Gelir ekleme yetkiniz yok.');
            go(routeFromReq());
        }
        if ($type === 'Expense' && !hasPerm($user, 'perm_add_expense')) {
            flash('err', 'Gider ekleme yetkiniz yok.');
            go(routeFromReq());
        }
        [$accType, $accId] = parseAccountKey((string)($_POST['account_key'] ?? ''));
        if (!$accType || !$accId || !canAccessAccount($user, $accType, $accId)) {
            flash('err', 'Hesap seçimi geçersiz.');
            go(routeFromReq());
        }
        $amount = parseAmount((string)($_POST['amount'] ?? ''));
        $currency = trim((string)($_POST['currency'] ?? 'TRY'));
        $desc = trim((string)($_POST['description'] ?? ''));
        $dateSql = normDate((string)($_POST['transaction_date'] ?? ''));
        if ($amount <= 0 || !in_array($currency, $SUPPORTED_CURRENCIES, true)) {
            flash('err', 'Tutar veya para birimi geçersiz.');
            go(routeFromReq());
        }
        if ($desc === '') {
            $desc = $type === 'Income' ? 'Manuel Gelir' : 'Manuel Gider';
        }
        try {
            $tenant->beginTransaction();
            $tenant->prepare("SELECT id FROM financial_accounts WHERE id=:id AND profile_id=:pid FOR UPDATE")
                ->execute([':id' => $accId, ':pid' => $profileId]);
            if ($type === 'Expense') {
                $bal = getBalance($tenant, $profileId, $accId, $currency);
                if ($bal < $amount) {
                    throw new RuntimeException("Yetersiz bakiye. Mevcut: {$bal} {$currency}");
                }
            }
            addFinTrans($tenant, $profileId, $accId, $dateSql, $type, $amount, $currency, $amount, $desc, null, null);
            $tenant->commit();
            flash('ok', 'İşlem kaydedildi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'İşlem hatası: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'transfer_funds') {
        if (!hasPerm($user, 'perm_transfer')) {
            flash('err', 'Transfer yetkiniz yok.');
            go(routeFromReq());
        }
        [$sType, $sId] = parseAccountKey((string)($_POST['source_account_key'] ?? ''));
        [$dType, $dId] = parseAccountKey((string)($_POST['dest_account_key'] ?? ''));
        $amount = parseAmount((string)($_POST['amount'] ?? ''));
        $currency = trim((string)($_POST['currency'] ?? 'TRY'));
        $desc = trim((string)($_POST['description'] ?? ''));
        $dateSql = normDate((string)($_POST['transaction_date'] ?? ''));
        if (!$sType || !$sId || !$dType || !$dId || $sId === $dId) {
            flash('err', 'Kaynak/hedef hesap seçimi hatalı.');
            go(routeFromReq());
        }
        if (!canAccessAccount($user, $sType, $sId) || !canAccessAccount($user, $dType, $dId)) {
            flash('err', 'Hesap erişim yetkiniz yok.');
            go(routeFromReq());
        }
        if ($amount <= 0 || !in_array($currency, $SUPPORTED_CURRENCIES, true)) {
            flash('err', 'Tutar/para birimi geçersiz.');
            go(routeFromReq());
        }
        try {
            $src = getAccount($tenant, $profileId, $sId);
            $dst = getAccount($tenant, $profileId, $dId);
            if (!$src || !$dst) {
                throw new RuntimeException('Hesap bulunamadı.');
            }
            $tid = uuid4();
            if ($desc === '') {
                $desc = "{$src['name']} -> {$dst['name']} transfer";
            }
            $tenant->beginTransaction();
            $lockIds = [$sId, $dId];
            sort($lockIds);
            foreach ($lockIds as $lid) {
                $tenant->prepare("SELECT id FROM financial_accounts WHERE id=:id AND profile_id=:pid FOR UPDATE")->execute([':id' => $lid, ':pid' => $profileId]);
            }
            $bal = getBalance($tenant, $profileId, $sId, $currency);
            if ($bal < $amount) {
                throw new RuntimeException("Kaynak hesapta yetersiz bakiye. Mevcut {$bal} {$currency}");
            }
            addFinTrans($tenant, $profileId, $sId, $dateSql, 'Expense', $amount, $currency, $amount, $desc, null, $tid);
            addFinTrans($tenant, $profileId, $dId, $dateSql, 'Income', $amount, $currency, $amount, $desc, null, $tid);
            $tenant->commit();
            flash('ok', 'Transfer tamamlandı.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Transfer hatası: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'add_person') {
        if (!hasPerm($user, 'perm_contacts')) {
            flash('err', 'Müteri yönetimi yetkiniz yok.');
            go(routeFromReq());
        }
        $full = trim((string)($_POST['full_name'] ?? ''));
        $phone = trim((string)($_POST['phone'] ?? ''));
        $addr = trim((string)($_POST['address'] ?? ''));
        $notes = trim((string)($_POST['notes'] ?? ''));
        if ($full === '') {
            flash('err', 'Ad soyad zorunlu.');
            go(routeFromReq());
        }
        try {
            $newPersonId = uuid4();
            $tenant->prepare("INSERT INTO persons (id,profile_id,full_name,phone_number,address,notes,is_deleted,created_at,updated_at) VALUES (:id,:pid,:f,:p,:a,:n,0,NOW(),NOW())")
                ->execute([':id' => $newPersonId, ':pid' => $profileId, ':f' => $full, ':p' => $phone, ':a' => $addr, ':n' => $notes]);
            $afterRow = syncFetchRowSnapshot($tenant, 'persons', $profileId, $newPersonId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'persons',
                'ADD',
                $newPersonId,
                null,
                $afterRow,
                ['source' => 'web.add_person']
            );
            flash('ok', 'Müteri eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Müteri eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'update_person') {
        if (!hasPerm($user, 'perm_contacts')) {
            flash('err', 'Musteri yonetimi yetkiniz yok.');
            go(routeFromReq());
        }
        $personId = trim((string)($_POST['person_id'] ?? ''));
        $full = trim((string)($_POST['full_name'] ?? ''));
        $phone = trim((string)($_POST['phone'] ?? ''));
        $addr = trim((string)($_POST['address'] ?? ''));
        $notes = trim((string)($_POST['notes'] ?? ''));
        if ($personId === '' || $full === '') {
            flash('err', 'Musteri ve ad soyad zorunludur.');
            go(routeFromReq());
        }
        try {
            $st = $tenant->prepare("SELECT full_name FROM persons WHERE id=:id AND profile_id=:pid AND is_deleted=0 LIMIT 1");
            $st->execute([':id' => $personId, ':pid' => $profileId]);
            $row = $st->fetch();
            if (!$row) {
                throw new RuntimeException('Musteri kaydi bulunamadi.');
            }
            $oldName = trim((string)($row['full_name'] ?? ''));
            $beforeRow = syncFetchRowSnapshot($tenant, 'persons', $profileId, $personId);

            $tenant->beginTransaction();
            $tenant->prepare("UPDATE persons SET full_name=:f,phone_number=:p,address=:a,notes=:n,updated_at=NOW() WHERE id=:id AND profile_id=:pid AND is_deleted=0")
                ->execute([':f' => $full, ':p' => $phone, ':a' => $addr, ':n' => $notes, ':id' => $personId, ':pid' => $profileId]);

            if ($oldName !== '' && strcasecmp($oldName, $full) !== 0) {
                $tenant->prepare("UPDATE customer_history_events SET person_full_name=:new_name WHERE profile_id=:pid AND (person_id=:person_id OR person_full_name=:old_name)")
                    ->execute([':new_name' => $full, ':pid' => $profileId, ':person_id' => $personId, ':old_name' => $oldName]);
                $tenant->prepare("UPDATE debt_records SET person_full_name=:new_name WHERE profile_id=:pid AND (person_id=:person_id OR person_full_name=:old_name)")
                    ->execute([':new_name' => $full, ':pid' => $profileId, ':person_id' => $personId, ':old_name' => $oldName]);
            }
            $afterRow = syncFetchRowSnapshot($tenant, 'persons', $profileId, $personId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'persons',
                'UPDATE',
                $personId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.update_person']
            );
            $tenant->commit();
            flash('ok', 'Musteri guncellendi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Musteri guncellenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'delete_person') {
        if (!hasPerm($user, 'perm_contacts')) {
            flash('err', 'Musteri yonetimi yetkiniz yok.');
            go(routeFromReq());
        }
        $personId = trim((string)($_POST['person_id'] ?? ''));
        if ($personId === '') {
            flash('err', 'Silinecek musteri secilmedi.');
            go(routeFromReq());
        }
        $route = routeFromReq();
        unset($route['person'], $route['op']);
        try {
            $beforeRow = syncFetchRowSnapshot($tenant, 'persons', $profileId, $personId);
            $tenant->prepare("UPDATE persons SET is_deleted=1,updated_at=NOW() WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $personId, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'persons', $profileId, $personId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'persons',
                'UPDATE',
                $personId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.delete_person']
            );
            flash('ok', 'Musteri silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Musteri silinemedi: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'customer_cash_txn') {
        if (!hasPerm($user, 'perm_accounts')) {
            flash('err', 'Cari hesap yetkiniz yok.');
            go(routeFromReq());
        }
        $personName = trim((string)($_POST['person_name'] ?? ''));
        $direction = trim((string)($_POST['cash_direction'] ?? 'CashIn'));
        $hocaType = strtolower(trim((string)($_POST['payment_type'] ?? $_POST['hoca_type'] ?? 'cash')));
        $amount = parseAmount((string)($_POST['amount'] ?? '0'));
        $currency = strtoupper(trim((string)($_POST['currency'] ?? 'TRY')));
        $description = trim((string)($_POST['description'] ?? ''));
        $dateSql = normDate((string)($_POST['transaction_date'] ?? ''));
        $route = routeFromReq();
        if ($personName !== '') {
            $route['person'] = $personName;
        }
        $route['op'] = 'cash';
        if (!in_array($direction, ['CashIn', 'CashOut'], true) || !in_array($hocaType, ['cash', 'card', 'transfer'], true) || $personName === '' || $amount <= 0 || !in_array($currency, $SUPPORTED_CURRENCIES, true)) {
            flash('err', 'Nakit verisi gecersiz.');
            go($route);
        }
        try {
            $personId = personIdByName($tenant, $profileId, $personName);
            if (!$personId) {
                throw new RuntimeException('Musteri bulunamadi.');
            }
            [$accType, $accId] = parseAccountKey((string)($_POST['source_account_key'] ?? ''));
            if (!$accType || !$accId || !canAccessAccount($user, $accType, $accId)) {
                throw new RuntimeException('Kasa/Kart secimi gecersiz.');
            }
            if ($hocaType === 'cash' && $accType !== 'cash') {
                throw new RuntimeException('Nakit seciminde kasa hesabi zorunludur.');
            }
            if (in_array($hocaType, ['card', 'transfer'], true) && $accType !== 'card') {
                throw new RuntimeException('Kart/Havale seciminde kart hesabi zorunludur.');
            }
            $affectCash = !empty($_POST['affect_cash']); // KASAYI ETKİLE CHECKBOX'I
            $isIn = ($direction === 'CashIn');
            $histDebt = $isIn ? 0.0 : $amount;
            $histCredit = $isIn ? $amount : 0.0;

            // PYTHON İLE BİREBİR AYNI İSİMLENDİRME
            $eventType = $isIn ? 'NAKİT GİRİŞİ (TAHSİLAT)' : 'NAKİT ÇIKIŞI (ÖDEME)';
            $finType = $isIn ? 'Income' : 'Expense';
            $eventDesc = $description !== '' ? $description : (($hocaType === 'cash' ? 'Nakit' : ($hocaType === 'card' ? 'Kart' : 'Havale')) . ' işlemi');

            $tenant->beginTransaction();
            $finId = null;

            if ($affectCash) {
                $tenant->prepare("SELECT id FROM financial_accounts WHERE id=:id AND profile_id=:pid FOR UPDATE")->execute([':id' => $accId, ':pid' => $profileId]);
                if (!$isIn) {
                    $bal = getBalance($tenant, $profileId, $accId, $currency);
                    if ($bal < $amount) {
                        throw new RuntimeException("Yetersiz bakiye. Mevcut: {$bal} {$currency}");
                    }
                }
                $finId = addFinTrans($tenant, $profileId, $accId, $dateSql, $finType, $amount, $currency, $amount, "Cari Nakit: {$eventDesc}", $personId, null);
            }

            addHist($tenant, $profileId, $personId, $personName, $dateSql . ' ' . date('H:i:s'), $eventType, $eventDesc, $histDebt, $histCredit, $currency, $finId);
            $tenant->commit();
            flash('ok', 'Nakit işlemi kaydedildi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Nakit islemi hatasi: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'customer_stock_txn') {
        if (!hasPerm($user, 'perm_accounts')) {
            flash('err', 'Cari hesap yetkiniz yok.');
            go(routeFromReq());
        }
        $personName = trim((string)($_POST['person_name'] ?? ''));
        $stockType = normalizeStockType((string)($_POST['stock_type'] ?? 'Product'));
        $rawMode = trim((string)($_POST['stock_mode'] ?? 'In'));
        $itemName = trim((string)($_POST['item_name'] ?? ''));
        $qty = parseAmount((string)($_POST['quantity'] ?? '0'));
        $depot = trim((string)($_POST['depot_name'] ?? ''));
        $unitPrice = parseAmount((string)($_POST['unit_price'] ?? '0'));
        $currency = strtoupper(trim((string)($_POST['currency'] ?? 'TRY')));
        $description = trim((string)($_POST['description'] ?? ''));
        $dateSql = normDate((string)($_POST['transaction_date'] ?? ''));
        $barcodeCodes = $_POST['barcode_code_list'] ?? [];
        $barcodeGrams = $_POST['barcode_gram_list'] ?? [];
        $barcodeKarats = $_POST['barcode_karat_list'] ?? [];
        $paymentTypeList = $_POST['payment_type_list'] ?? ($_POST['bedel_type_list'] ?? []);
        $paymentKindList = $_POST['payment_kind_list'] ?? [];
        $paymentQtyList = $_POST['payment_qty_list'] ?? ($_POST['bedel_amount_list'] ?? []);
        $paymentUnitPriceList = $_POST['payment_unit_price_list'] ?? [];
        $paymentCurrencyList = $_POST['payment_currency_list'] ?? [];
        $paymentDepotList = $_POST['payment_depot_list'] ?? [];
        $paymentAccountList = $_POST['payment_account_key_list'] ?? [];
        $paymentGramList = $_POST['payment_gram_list'] ?? ($_POST['bedel_gram_list'] ?? []);
        $paymentKaratList = $_POST['payment_karat_list'] ?? ($_POST['bedel_karat_list'] ?? []);
        $affectStock = !empty($_POST['affect_stock']);
        $applyTaxes = !empty($_POST['apply_taxes']);
        $selectedTaxIdsRaw = $_POST['selected_tax_ids'] ?? [];
        $route = routeFromReq();
        if ($personName !== '') {
            $route['person'] = $personName;
        }
        $route['op'] = ($stockType === 'Product') ? 'gold' : strtolower($stockType);
        $modeMap = ['Purchase' => 'In', 'Sale' => 'Out', 'In' => 'In', 'Out' => 'Out', 'PaidIn' => 'PaidIn', 'PaidOut' => 'PaidOut'];
        if (!isset($modeMap[$rawMode])) {
            flash('err', 'Stok islem modu gecersiz.');
            go($route);
        }
        $modeShort = $modeMap[$rawMode];
        $isOut = in_array($modeShort, ['Out', 'PaidOut'], true);
        $isPaid = in_array($modeShort, ['PaidIn', 'PaidOut'], true);
        $usesPaymentFlow = in_array($modeShort, ['In', 'Out'], true);
        $usesBedelFlow = $isPaid;
        if ($personName === '' || $itemName === '' || $depot === '' || $qty <= 0) {
            flash('err', 'Musteri, tur, depo ve miktar zorunludur.');
            go($route);
        }
        if (!in_array($currency, $SUPPORTED_CURRENCIES, true)) {
            flash('err', 'Para birimi gecersiz.');
            go($route);
        }
        $barcodeRows = [];
        if ($stockType === 'Product') {
            $qtyInt = (int)round($qty);
            if ($qtyInt <= 0 || abs($qty - $qtyInt) > 0.0001) {
                flash('err', 'Altin islemlerinde miktar tam sayi olmalidir.');
                go($route);
            }
            if (!is_array($barcodeCodes) || !is_array($barcodeGrams) || !is_array($barcodeKarats) || count($barcodeCodes) !== $qtyInt) {
                flash('err', 'Miktar kadar barkod/gram/ayar girmelisiniz.');
                go($route);
            }
            $seenBarcode = [];
            for ($i = 0; $i < $qtyInt; $i++) {
                $bc = trim((string)($barcodeCodes[$i] ?? ''));
                $gr = parseAmount((string)($barcodeGrams[$i] ?? '0'));
                $kr = trim((string)($barcodeKarats[$i] ?? ''));
                if ($bc === '' || $gr <= 0 || $kr === '') {
                    flash('err', 'Altin barkod satirlarinda barkod, gram ve ayar zorunludur.');
                    go($route);
                }
                if (isset($seenBarcode[$bc])) {
                    flash('err', "Ayni barkod birden fazla girildi: {$bc}");
                    go($route);
                }
                $seenBarcode[$bc] = true;
                $barcodeRows[] = ['barcode' => $bc, 'gram' => $gr, 'karat' => $kr];
            }
            $qty = (float)$qtyInt;
        }

        $exchangeItems = [];
        $paymentRows = [];
        if ($usesPaymentFlow && is_array($paymentTypeList) && $paymentTypeList) {
            foreach ($paymentTypeList as $idx => $rawType) {
                $typ = trim((string)$rawType);
                $kind = strtolower(trim((string)($paymentKindList[$idx] ?? '')));
                $qtyVal = parseAmount((string)($paymentQtyList[$idx] ?? '0'));
                $unitVal = parseAmount((string)($paymentUnitPriceList[$idx] ?? '0'));
                $curVal = strtoupper(trim((string)($paymentCurrencyList[$idx] ?? $currency)));
                $depotVal = trim((string)($paymentDepotList[$idx] ?? $depot));
                $accountKey = trim((string)($paymentAccountList[$idx] ?? ''));
                $gramVal = parseAmount((string)($paymentGramList[$idx] ?? '0'));
                $karatVal = trim((string)($paymentKaratList[$idx] ?? ''));

                if ($typ === '' || $qtyVal <= 0) {
                    continue;
                }
                if ($depotVal === '') {
                    $depotVal = $depot;
                }

                $isCurrencyType = in_array(strtoupper($typ), $SUPPORTED_CURRENCIES, true);
                if ($isCurrencyType) {
                    $curVal = strtoupper($typ);
                } elseif ($curVal === '' || !in_array($curVal, $SUPPORTED_CURRENCIES, true)) {
                    $curVal = $currency;
                }
                if ($kind === '' && in_array(strtolower($typ), ['nakit', 'cash'], true)) {
                    $kind = 'cash';
                } elseif ($kind === '' && in_array(strtolower($typ), ['kart', 'card'], true)) {
                    $kind = 'card';
                } elseif ($kind === '' && in_array(strtolower($typ), ['havale', 'transfer'], true)) {
                    $kind = 'transfer';
                }
                if ($isCurrencyType && $kind === '') {
                    $kind = 'currency';
                }
                if (in_array($kind, ['cash', 'card', 'currency', 'transfer'], true)) {
                    $unitVal = 1.0;
                } elseif ($unitVal <= 0) {
                    flash('err', "Odeme satiri birim fiyati zorunludur: {$typ}");
                    go($route);
                }
                $lineTotal = $qtyVal * $unitVal;

                $stOfType = $isCurrencyType ? null : stockTypeFromDefinition($tenant, $profileId, $typ);
                if ($stOfType !== null && $kind === '') {
                    $kind = strtolower($stOfType);
                }
                if ($kind === '') {
                    $kind = 'currency';
                }

                if ($stOfType === 'Product' || $kind === 'product') {
                    if ($gramVal <= 0 || $karatVal === '') {
                        flash('err', "Odeme turu altin ise gram ve ayar zorunludur: {$typ}");
                        go($route);
                    }
                }
                if (in_array($kind, ['cash', 'card', 'transfer', 'currency'], true)) {
                    [$payType, $payId] = parseAccountKey($accountKey);
                    if (!$payType || !$payId || !canAccessAccount($user, $payType, $payId)) {
                        flash('err', "Odeme hesabi secimi gecersiz: {$typ}");
                        go($route);
                    }
                    if (in_array($kind, ['card', 'transfer'], true) && $payType !== 'card') {
                        flash('err', "Kart/Havale odeme icin kart hesabi zorunludur: {$typ}");
                        go($route);
                    }
                }
                if (in_array($kind, ['product', 'ziynet', 'scrap'], true) && $stOfType === null) {
                    flash('err', "Odeme urunu tanimlarda bulunamadi: {$typ}");
                    go($route);
                }
                if (in_array($kind, ['product', 'ziynet', 'scrap'], true) && $depotVal === '') {
                    flash('err', "Odeme satiri icin depo zorunludur: {$typ}");
                    go($route);
                }

                $item = ['type' => ($isCurrencyType ? strtoupper($typ) : $typ), 'amount' => $qtyVal];
                if ($stOfType !== null) {
                    $item['stock_type'] = $stOfType;
                }
                if ($gramVal > 0) {
                    $item['gram'] = $gramVal;
                }
                if ($karatVal !== '') {
                    $item['karat'] = $karatVal;
                }
                if (in_array($kind, ['cash', 'card', 'currency', 'transfer'], true)) {
                    $item['type'] = strtoupper($curVal);
                    $item['amount'] = $lineTotal;
                }
                $exchangeItems[] = $item;
                $paymentRows[] = [
                    'kind' => $kind,
                    'type' => $typ,
                    'qty' => $qtyVal,
                    'unit_price' => $unitVal,
                    'currency' => $curVal,
                    'line_total' => $lineTotal,
                    'account_key' => $accountKey,
                    'depot' => $depotVal,
                    'stock_type' => $stOfType,
                    'gram' => $gramVal,
                    'karat' => $karatVal,
                ];
            }
        }
        if ($usesBedelFlow && is_array($paymentTypeList) && $paymentTypeList) {
            foreach ($paymentTypeList as $idx => $rawType) {
                $typ = trim((string)$rawType);
                $amt = parseAmount((string)($paymentQtyList[$idx] ?? '0'));
                $gramVal = parseAmount((string)($paymentGramList[$idx] ?? '0'));
                $karatVal = trim((string)($paymentKaratList[$idx] ?? ''));
                if ($typ === '' || $amt <= 0) {
                    continue;
                }
                $item = ['type' => $typ, 'amount' => $amt];
                $stOfType = stockTypeFromDefinition($tenant, $profileId, $typ);
                if ($stOfType !== null) {
                    $item['stock_type'] = $stOfType;
                }
                if ($stOfType === 'Product') {
                    if ($gramVal <= 0 || $karatVal === '') {
                        flash('err', "Bedel turu altin ise gram ve ayar zorunludur: {$typ}");
                        go($route);
                    }
                }
                if ($gramVal > 0) {
                    $item['gram'] = $gramVal;
                }
                if ($karatVal !== '') {
                    $item['karat'] = $karatVal;
                }
                $exchangeItems[] = $item;
            }
        }
        if ($usesPaymentFlow && !$paymentRows) {
            flash('err', 'Alis/Satis islemlerinde en az bir odeme satiri zorunludur.');
            go($route);
        }

        $ratesPayload = fetchDashboardRates($tenant, $profileId);
        $mainTotal = $qty * $unitPrice;
        $taxSnapshot = [
            'version' => 2,
            'apply_taxes' => false,
            'currency' => $currency,
            'customer_name' => $personName,
            'note' => $description,
            'subtotal' => round($mainTotal, 2),
            'tax_total' => 0.0,
            'grand_total' => round($mainTotal, 2),
            'applied_taxes_summary' => '',
            'taxes' => [],
            'fixed_items' => [],
            'lines' => [[
                'name' => $itemName,
                'qty' => $qty,
                'unit_price' => $unitPrice,
                'line_subtotal' => round($mainTotal, 2),
            ]],
        ];
        if ($usesPaymentFlow && $applyTaxes) {
            $selectedTaxes = fetchActiveTaxesByIds($tenant, $profileId, is_array($selectedTaxIdsRaw) ? $selectedTaxIdsRaw : []);
            if (!$selectedTaxes) {
                flash('err', 'Vergi secimi yapildi ancak gecerli aktif vergi bulunamadi.');
                go($route);
            }
            $taxSnapshot = computeTaxSnapshot(
                $mainTotal,
                $currency,
                $selectedTaxes,
                $personName,
                $description,
                [[
                    'name' => $itemName,
                    'qty' => $qty,
                    'unit_price' => $unitPrice,
                    'line_subtotal' => round($mainTotal, 2),
                ]]
            );
        }
        $expectedPaymentTotal = ($usesPaymentFlow && !empty($taxSnapshot['apply_taxes']))
            ? (float)($taxSnapshot['grand_total'] ?? $mainTotal)
            : $mainTotal;
        $paidTotalMain = 0.0;
        if ($usesPaymentFlow) {
            if ($mainTotal <= 0) {
                flash('err', 'Alis/Satis islemlerinde ana urun birim fiyat ve miktar zorunludur.');
                go($route);
            }
            foreach ($paymentRows as $pIdx => $prow) {
                $converted = convertCurrencyAmount((float)$prow['line_total'], (string)$prow['currency'], $currency, $ratesPayload);
                if ($converted === null) {
                    flash('err', 'Odeme satirinda kur cevirimi yapilamadi.');
                    go($route);
                }
                $paymentRows[$pIdx]['line_total_main'] = $converted;
                $paidTotalMain += (float)$converted;
            }
            if (abs($paidTotalMain - $expectedPaymentTotal) > 0.05) {
                flash('err', 'Odeme toplami ile islem tutari (vergi dahil) eslesmelidir.');
                go($route);
            }
        }
        try {
            $personId = personIdByName($tenant, $profileId, $personName);
            if (!$personId) {
                throw new RuntimeException('Musteri bulunamadi.');
            }
            $prefix = $stockType === 'Product' ? 'Product' : ($stockType === 'Ziynet' ? 'Ziynet' : 'Scrap');
            $mode = $prefix . $modeShort;
            $hasPayments = (bool)$exchangeItems;
            $eventType = stockTypeLabel($stockType) . ' ' . ($isOut ? 'CIKIS' : 'GIRIS');
            if ($isPaid) {
                $eventType .= ' (BEDELLI)';
            } else {
                $eventType .= $hasPayments ? ' (ODEMELI)' : ($isOut ? ' (SATIS)' : ' (ALIS)');
            }
            $mainDesc = $description !== '' ? $description : (formatAmountByType($qty, $itemName, $SUPPORTED_CURRENCIES) . ' ' . $itemName);
            $stockEffects = [];
            if ($affectStock) {
                $mainEffect = ['direction' => ($isOut ? 'out' : 'in'), 'stock_type' => $stockType, 'item_name' => $itemName, 'depot' => $depot, 'qty' => $qty, 'price' => $unitPrice];
                if ($stockType === 'Product' && $barcodeRows) {
                    $mainEffect['barcodes'] = $barcodeRows;
                }
                $stockEffects[] = $mainEffect;
                if ($usesPaymentFlow && $paymentRows) {
                    $paymentDir = $isOut ? 'in' : 'out';
                    foreach ($paymentRows as $prow) {
                        $pKind = strtolower(trim((string)($prow['kind'] ?? '')));
                        if (!in_array($pKind, ['product', 'ziynet', 'scrap'], true)) {
                            continue;
                        }
                        $pType = trim((string)($prow['type'] ?? ''));
                        $pQty = (float)($prow['qty'] ?? 0);
                        $pDepot = trim((string)($prow['depot'] ?? $depot));
                        if ($pType === '' || $pQty <= 0 || $pDepot === '') {
                            continue;
                        }
                        $priceConverted = convertCurrencyAmount(
                            (float)($prow['unit_price'] ?? 0),
                            (string)($prow['currency'] ?? $currency),
                            $currency,
                            $ratesPayload
                        );
                        $pPrice = $priceConverted === null ? (float)($prow['unit_price'] ?? 0) : (float)$priceConverted;
                        $stockTypeFromRow = normalizeStockType((string)($prow['stock_type'] ?? $pKind));
                        $stockEffects[] = [
                            'direction' => $paymentDir,
                            'stock_type' => $stockTypeFromRow,
                            'item_name' => $pType,
                            'depot' => $pDepot,
                            'qty' => $pQty,
                            'price' => $pPrice,
                        ];
                    }
                }
            }
            $jsonDesc = json_encode([
                'main_desc' => $mainDesc,
                'mode' => $mode,
                'exchange_items' => $exchangeItems,
                'payments' => $paymentRows,
                'tax_snapshot' => $taxSnapshot,
                'main_product' => $itemName,
                'main_qty' => $qty,
                'main_depot' => $depot,
                'gold_details' => $barcodeRows,
                'stock_effects' => $stockEffects,
            ], JSON_UNESCAPED_UNICODE);
            $tenant->beginTransaction();
            foreach ($stockEffects as $eff) {
                if (!is_array($eff)) {
                    continue;
                }
                if (normalizeStockType((string)($eff['stock_type'] ?? '')) !== 'Product') {
                    continue;
                }
                $effBarcodes = $eff['barcodes'] ?? [];
                if (!is_array($effBarcodes) || !$effBarcodes) {
                    continue;
                }
                $effDir = strtolower(trim((string)($eff['direction'] ?? '')));
                $effItem = trim((string)($eff['item_name'] ?? ''));
                $effDepot = trim((string)($eff['depot'] ?? ''));
                foreach ($effBarcodes as $br) {
                    if (!is_array($br)) {
                        continue;
                    }
                    $bc = trim((string)($br['barcode'] ?? ''));
                    if ($bc === '') {
                        continue;
                    }
                    if ($effDir === 'out') {
                        $st = $tenant->prepare("SELECT id FROM stock_items WHERE profile_id=:pid AND stock_type='Product' AND unique_barcode=:bc AND is_deleted=0 AND quantity>0 LIMIT 1 FOR UPDATE");
                        $st->execute([':pid' => $profileId, ':bc' => $bc]);
                        if (!$st->fetch()) {
                            throw new RuntimeException("Bu barkod stokta bulunamadi: {$bc}");
                        }
                    } elseif ($effDir === 'in') {
                        if (barcodeExistsInStock($tenant, $profileId, $bc) || barcodeExistsInHistory($tenant, $profileId, $bc)) {
                            throw new RuntimeException("Bu barkod daha onceden tanimlanmis: {$bc}");
                        }
                    }
                }
            }
            if ($stockEffects) {
                applyStockEffects($tenant, $profileId, $stockEffects, false);
            }
            if ($usesPaymentFlow && $hasPayments && $paymentRows) {
                $accountLocks = [];
                $needByAccount = [];
                $cashPostings = [];
                foreach ($paymentRows as $prow) {
                    $kind = strtolower(trim((string)($prow['kind'] ?? '')));
                    if (!in_array($kind, ['cash', 'card', 'transfer', 'currency'], true)) {
                        continue;
                    }
                    $accKey = trim((string)($prow['account_key'] ?? ''));
                    $curr = strtoupper(trim((string)($prow['currency'] ?? $currency)));
                    $amt = (float)($prow['line_total'] ?? 0);
                    if ($amt <= 0 || !in_array($curr, $SUPPORTED_CURRENCIES, true)) {
                        continue;
                    }
                    if ($accKey === '') {
                        throw new RuntimeException('Odeme satirinda hesap secimi zorunludur.');
                    }
                    [$accTypeRow, $accIdRow] = parseAccountKey($accKey);
                    if (!$accTypeRow || !$accIdRow || !canAccessAccount($user, $accTypeRow, $accIdRow)) {
                        throw new RuntimeException('Odeme satiri hesap secimi gecersiz.');
                    }
                    if (in_array($kind, ['card', 'transfer'], true) && $accTypeRow !== 'card') {
                        throw new RuntimeException('Kart/Havale odeme satiri kart hesabi ister.');
                    }
                    if (!isset($accountLocks[$accIdRow])) {
                        $tenant->prepare("SELECT id FROM financial_accounts WHERE id=:id AND profile_id=:pid FOR UPDATE")
                            ->execute([':id' => $accIdRow, ':pid' => $profileId]);
                        $accountLocks[$accIdRow] = $accTypeRow;
                    }
                    $cashPostings[] = ['acc_id' => $accIdRow, 'currency' => $curr, 'amount' => $amt];
                    if (!$isOut) {
                        $needByAccount[$accIdRow][$curr] = ($needByAccount[$accIdRow][$curr] ?? 0.0) + $amt;
                    }
                }
                if (!$isOut) {
                    foreach ($needByAccount as $accIdRow => $needByCurrency) {
                        foreach ($needByCurrency as $needCurr => $needAmt) {
                            $bal = getBalance($tenant, $profileId, (string)$accIdRow, (string)$needCurr);
                            if ($bal + 0.0001 < (float)$needAmt) {
                                throw new RuntimeException("Odeme icin yetersiz bakiye: {$needCurr}");
                            }
                        }
                    }
                }
                $finType = $isOut ? 'Income' : 'Expense';
                foreach ($cashPostings as $cp) {
                    $descPay = 'Cari ' . stockTypeLabel($stockType) . ' odeme';
                    addFinTrans(
                        $tenant,
                        $profileId,
                        (string)$cp['acc_id'],
                        $dateSql,
                        $finType,
                        (float)$cp['amount'],
                        (string)$cp['currency'],
                        (float)$cp['amount'],
                        $descPay,
                        $personId,
                        null
                    );
                }
            }
            $histId = addHist($tenant, $profileId, $personId, $personName, $dateSql . ' ' . date('H:i:s'), $eventType, $jsonDesc ?: $mainDesc, 0.0, 0.0, 'Karma', null);
            $taxSourceTable = 'customer_history_events';
            $taxSourceId = $histId;
            if ($usesPaymentFlow && $isOut) {
                $saleId = uuid4();
                $paymentSummary = [];
                foreach ($paymentRows as $prow) {
                    $typ = trim((string)($prow['type'] ?? ''));
                    $amt = (float)($prow['line_total'] ?? 0);
                    $cur = strtoupper(trim((string)($prow['currency'] ?? $currency)));
                    if ($typ === '' || $amt <= 0) {
                        continue;
                    }
                    $paymentSummary[] = $typ . ': ' . number_format($amt, 2, '.', '') . ' ' . $cur;
                }
                $productsPayload = [
                    'items' => [[
                        'name' => $itemName,
                        'qty' => $qty,
                        'unit_price' => $unitPrice,
                        'currency' => $currency,
                        'stock_type' => $stockType,
                        'depot' => $depot,
                    ]],
                    'gold_details' => $barcodeRows,
                    'payments' => $paymentRows,
                    'history_event_id' => $histId,
                ];
                $tenant->prepare(
                    "INSERT INTO sales (id,profile_id,sale_date,customer_full_name,total_amount,currency,payment_method_summary,products_json,is_deleted,created_at,updated_at)
                     VALUES (:id,:pid,:d,:c,:t,:cur,:p,:j,0,NOW(),NOW())"
                )->execute([
                    ':id' => $saleId,
                    ':pid' => $profileId,
                    ':d' => $dateSql,
                    ':c' => $personName,
                    ':t' => (float)($taxSnapshot['grand_total'] ?? $mainTotal),
                    ':cur' => $currency,
                    ':p' => implode(', ', $paymentSummary),
                    ':j' => json_encode($productsPayload, JSON_UNESCAPED_UNICODE),
                ]);
                $saleAfterRow = syncFetchRowSnapshot($tenant, 'sales', $profileId, $saleId);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'sales',
                    'ADD',
                    $saleId,
                    null,
                    $saleAfterRow,
                    ['source' => 'web.customer_stock_txn.sale']
                );
                $taxSourceTable = 'sales';
                $taxSourceId = $saleId;
            }

            if (!empty($taxSnapshot['apply_taxes'])) {
                $taxApplicationId = uuid4();
                $taxSnapshotStore = $taxSnapshot;
                $taxSnapshotStore['source_table'] = $taxSourceTable;
                $taxSnapshotStore['source_id'] = $taxSourceId;
                $taxSnapshotStore['tax_application_id'] = $taxApplicationId;
                $taxSnapshotJson = json_encode($taxSnapshotStore, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                if (!is_string($taxSnapshotJson)) {
                    $taxSnapshotJson = '{}';
                }
                $tenant->prepare(
                    "INSERT INTO tax_applications (id,source_table,source_id,profile_id,subtotal,tax_total,grand_total,currency,snapshot_json,created_at)
                     VALUES (:id,:tbl,:sid,:pid,:sub,:tax,:grand,:cur,:snap,NOW())"
                )->execute([
                    ':id' => $taxApplicationId,
                    ':tbl' => $taxSourceTable,
                    ':sid' => $taxSourceId,
                    ':pid' => $profileId,
                    ':sub' => (float)($taxSnapshot['subtotal'] ?? $mainTotal),
                    ':tax' => (float)($taxSnapshot['tax_total'] ?? 0),
                    ':grand' => (float)($taxSnapshot['grand_total'] ?? $mainTotal),
                    ':cur' => $currency,
                    ':snap' => $taxSnapshotJson,
                ]);
                $taxAfterRow = syncFetchRowSnapshot($tenant, 'tax_applications', $profileId, $taxApplicationId);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'tax_applications',
                    'ADD',
                    $taxApplicationId,
                    null,
                    $taxAfterRow,
                    ['source' => 'web.customer_stock_txn.tax_application']
                );
            }
            $tenant->commit();
            flash('ok', 'Stok/cari islemi kaydedildi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Stok/cari islem hatasi: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'customer_exchange') {
        if (!hasPerm($user, 'perm_accounts')) {
            flash('err', 'Cari hesap yetkiniz yok.');
            go(routeFromReq());
        }
        $personName = trim((string)($_POST['person_name'] ?? ''));
        $mode = trim((string)($_POST['exchange_mode'] ?? 'ExchangePayable'));
        $outType = trim((string)($_POST['out_type'] ?? ''));
        $inType = trim((string)($_POST['in_type'] ?? ''));
        $outKind = strtolower(trim((string)($_POST['out_kind'] ?? '')));
        $inKind = strtolower(trim((string)($_POST['in_kind'] ?? '')));
        $outAmount = parseAmount((string)($_POST['out_amount'] ?? '0'));
        $inAmount = parseAmount((string)($_POST['in_amount'] ?? '0'));
        $outGram = parseAmount((string)($_POST['out_gram'] ?? '0'));
        $inGram = parseAmount((string)($_POST['in_gram'] ?? '0'));
        $outKarat = trim((string)($_POST['out_karat'] ?? ''));
        $inKarat = trim((string)($_POST['in_karat'] ?? ''));
        $description = trim((string)($_POST['description'] ?? ''));
        $dateSql = normDate((string)($_POST['transaction_date'] ?? ''));
        $depotName = trim((string)($_POST['depot_name'] ?? ui_text('default_depot_name', 'Merkez', 'Central')));
        $affectStock = !empty($_POST['affect_stock']);
        if ($depotName === '') {
            $depotName = ui_text('default_depot_name', 'Merkez', 'Central');
        }
        $route = routeFromReq();
        if ($personName !== '') {
            $route['person'] = $personName;
        }
        $route['op'] = 'exchange';
        $kindToStockType = static function (string $kind): ?string {
            return match ($kind) {
                'product' => 'Product',
                'ziynet' => 'Ziynet',
                'scrap' => 'Scrap',
                default => null,
            };
        };
        try {
            $outStockType = $kindToStockType($outKind);
            $inStockType = $kindToStockType($inKind);
            if ($outStockType === null) {
                $outStockType = stockTypeFromDefinition($tenant, $profileId, $outType);
            }
            if ($inStockType === null) {
                $inStockType = stockTypeFromDefinition($tenant, $profileId, $inType);
            }
        } catch (Throwable $ex) {
            flash('err', 'Ceviri turleri okunamadi: ' . $ex->getMessage());
            go($route);
        }
        if (!in_array($mode, ['ExchangePayable', 'ExchangeReceivable'], true) || $personName === '' || $outType === '' || $inType === '') {
            flash('err', 'Ceviri verisi gecersiz.');
            go($route);
        }
        if ($outAmount <= 0 || $inAmount <= 0) {
            flash('err', 'Ceviri miktarlari 0 dan buyuk olmalidir.');
            go($route);
        }
        if ($outStockType === 'Product') {
            $outInt = (int)round($outAmount);
            if ($outInt <= 0 || abs($outAmount - $outInt) > 0.0001) {
                flash('err', 'Kaynak altin icin adet tam sayi olmalidir.');
                go($route);
            }
            $outAmount = (float)$outInt;
        }
        if ($inStockType === 'Product') {
            $inInt = (int)round($inAmount);
            if ($inInt <= 0 || abs($inAmount - $inInt) > 0.0001) {
                flash('err', 'Hedef altin icin adet tam sayi olmalidir.');
                go($route);
            }
            $inAmount = (float)$inInt;
        }
        if ($outStockType === 'Product' && ($outGram <= 0 || $outKarat === '')) {
            flash('err', 'Verilen tur altin ise adet, gram ve ayar zorunludur.');
            go($route);
        }
        if ($inStockType === 'Product' && ($inGram <= 0 || $inKarat === '')) {
            flash('err', 'Alinan tur altin ise adet, gram ve ayar zorunludur.');
            go($route);
        }
        if ($outStockType === 'Scrap' && $outGram <= 0) {
            $outGram = $outAmount;
        }
        if ($inStockType === 'Scrap' && $inGram <= 0) {
            $inGram = $inAmount;
        }
        try {
            $personId = personIdByName($tenant, $profileId, $personName);
            if (!$personId) {
                throw new RuntimeException('Musteri bulunamadi.');
            }
            $outItem = ['type' => $outType, 'amount' => $outAmount, 'side' => 'source'];
            $inItem = ['type' => $inType, 'amount' => $inAmount, 'side' => 'dest'];
            if ($outStockType === 'Product' && $outGram > 0 && $outKarat !== '') {
                $outItem['gram'] = $outGram;
                $outItem['karat'] = $outKarat;
            } elseif ($outStockType === 'Scrap') {
                $outItem['gram'] = $outAmount;
            }
            if ($inStockType === 'Product' && $inGram > 0 && $inKarat !== '') {
                $inItem['gram'] = $inGram;
                $inItem['karat'] = $inKarat;
            } elseif ($inStockType === 'Scrap') {
                $inItem['gram'] = $inAmount;
            }
            $stockEffects = [];
            if ($affectStock) {
                if ($outStockType !== null) {
                    $stockEffects[] = ['direction' => 'out', 'stock_type' => $outStockType, 'item_name' => $outType, 'depot' => $depotName, 'qty' => $outAmount, 'price' => 0.0];
                }
                if ($inStockType !== null) {
                    $stockEffects[] = ['direction' => 'in', 'stock_type' => $inStockType, 'item_name' => $inType, 'depot' => $depotName, 'qty' => $inAmount, 'price' => 0.0];
                }
            }
            $descJson = json_encode([
                'main_desc' => ($description !== '' ? $description : 'Ceviri islemi'),
                'exchange_items' => [$outItem, $inItem],
                'mode' => $mode,
                'depot' => $depotName,
                'stock_effects' => $stockEffects,
            ], JSON_UNESCAPED_UNICODE);
            $tenant->beginTransaction();
            if ($stockEffects) {
                applyStockEffects($tenant, $profileId, $stockEffects, false);
            }
            addHist($tenant, $profileId, $personId, $personName, $dateSql . ' ' . date('H:i:s'), ($mode === 'ExchangePayable' ? 'CEVIRI (BORC)' : 'CEVIRI (ALACAK)'), $descJson ?: '', 0.0, 0.0, 'Karma', null);
            $tenant->commit();
            flash('ok', 'Ceviri islemi kaydedildi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Ceviri hatasi: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'customer_virman' || $act === 'customer_iman') {
        if (!hasPerm($user, 'perm_accounts')) {
            flash('err', 'Cari hesap yetkiniz yok.');
            go(routeFromReq());
        }
        $sourcePerson = trim((string)($_POST['source_person'] ?? ''));
        $targetPerson = trim((string)($_POST['target_person'] ?? ''));
        $assetType = trim((string)($_POST['asset_type'] ?? 'TRY'));
        $amount = parseAmount((string)($_POST['amount'] ?? '0'));
        $modeRaw = trim((string)($_POST['virman_mode'] ?? $_POST['iman_mode'] ?? 'VirmanPayable'));
        $modeMap = [
            'ImanPayable' => 'VirmanPayable',
            'ImanReceivable' => 'VirmanReceivable',
            'VirmanPayable' => 'VirmanPayable',
            'VirmanReceivable' => 'VirmanReceivable',
        ];
        $mode = $modeMap[$modeRaw] ?? '';
        $dateSql = normDate((string)($_POST['transaction_date'] ?? ''));
        $gram = parseAmount((string)($_POST['asset_gram'] ?? '0'));
        $karat = trim((string)($_POST['asset_karat'] ?? ''));
        $desc = trim((string)($_POST['description'] ?? ''));
        $route = routeFromReq();
        if ($sourcePerson !== '') {
            $route['person'] = $sourcePerson;
        }
        $route['op'] = 'virman';
        try {
            $assetStockType = stockTypeFromDefinition($tenant, $profileId, $assetType);
        } catch (Throwable $ex) {
            flash('err', 'Virman varlik turu okunamadi: ' . $ex->getMessage());
            go($route);
        }
        if ($sourcePerson === '' || $targetPerson === '' || $sourcePerson === $targetPerson || $amount <= 0 || !in_array($mode, ['VirmanPayable', 'VirmanReceivable'], true)) {
            flash('err', 'Virman verisi gecersiz.');
            go($route);
        }
        if ($assetStockType === 'Product' && ($gram <= 0 || $karat === '')) {
            flash('err', 'Virman varlik turu altin ise gram ve ayar zorunludur.');
            go($route);
        }
        try {
            $sourceId = personIdByName($tenant, $profileId, $sourcePerson);
            $targetId = personIdByName($tenant, $profileId, $targetPerson);
            if (!$sourceId || !$targetId) {
                throw new RuntimeException('Virman musterileri bulunamadi.');
            }
            if ($mode === 'VirmanPayable') {
                $sourceDebt = 0.0; $sourceCredit = $amount; $targetDebt = $amount; $targetCredit = 0.0;
            } else {
                $sourceDebt = $amount; $sourceCredit = 0.0; $targetDebt = 0.0; $targetCredit = $amount;
            }
            $sourceDesc = $desc !== '' ? $desc : "VIRMAN CIKIS -> {$targetPerson}";
            $targetDesc = $desc !== '' ? $desc : "VIRMAN GIRIS <- {$sourcePerson}";
            if ($gram > 0 && $karat !== '') {
                $sourceDesc = json_encode(['main_desc' => $sourceDesc, 'mode' => $mode, 'asset_type' => $assetType, 'amount' => $amount, 'gram' => $gram, 'karat' => $karat, 'direction' => 'outgoing_virman'], JSON_UNESCAPED_UNICODE);
                $targetDesc = json_encode(['main_desc' => $targetDesc, 'mode' => $mode, 'asset_type' => $assetType, 'amount' => $amount, 'gram' => $gram, 'karat' => $karat, 'direction' => 'incoming_virman'], JSON_UNESCAPED_UNICODE);
            }
            $tenant->beginTransaction();
            addHist($tenant, $profileId, $sourceId, $sourcePerson, $dateSql . ' ' . date('H:i:s'), 'VIRMAN (GONDERILEN)', (string)$sourceDesc, $sourceDebt, $sourceCredit, $assetType, null);
            addHist($tenant, $profileId, $targetId, $targetPerson, $dateSql . ' ' . date('H:i:s'), 'VIRMAN (ALINAN)', (string)$targetDesc, $targetDebt, $targetCredit, $assetType, null);
            $tenant->commit();
            flash('ok', 'Virman islemi kaydedildi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Virman hatasi: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'customer_event_delete') {
        if (!hasPerm($user, 'perm_accounts')) {
            flash('err', 'Cari hesap yetkiniz yok.');
            go(routeFromReq());
        }
        $eventId = trim((string)($_POST['event_id'] ?? ''));
        $personName = trim((string)($_POST['person_name'] ?? ''));
        $route = routeFromReq();
        if ($personName !== '') {
            $route['person'] = $personName;
        }
        if ($eventId === '') {
            flash('err', 'Silinecek hareket secilmedi.');
            go($route);
        }
        try {
            $tenant->beginTransaction();
            $st = $tenant->prepare("SELECT * FROM customer_history_events WHERE id=:id AND profile_id=:pid LIMIT 1 FOR UPDATE");
            $st->execute([':id' => $eventId, ':pid' => $profileId]);
            $ev = $st->fetch();
            if (!$ev) {
                throw new RuntimeException('Hareket bulunamadi.');
            }
            $stockEffects = extractStockEffectsFromDesc((string)($ev['description'] ?? ''));
            if ($stockEffects) {
                applyStockEffects($tenant, $profileId, $stockEffects, true);
            }
            $rel = trim((string)($ev['related_id'] ?? ''));
            if ($rel !== '') {
                // Borç kaydı senkronizasyonu
                $debtBefore = syncFetchRowSnapshot($tenant, 'debt_records', $profileId, $rel);
                $tenant->prepare("UPDATE debt_records SET is_deleted=1,updated_at=NOW() WHERE id=:id AND profile_id=:pid")->execute([':id' => $rel, ':pid' => $profileId]);
                syncEnqueueGlobalEvent($tenant, $profileId, 'debt_records', 'UPDATE', $rel, $debtBefore, syncFetchRowSnapshot($tenant, 'debt_records', $profileId, $rel), ['source' => 'web.customer_event_delete']);
                
                // Kasa kaydı senkronizasyonu
                $finBefore = syncFetchRowSnapshot($tenant, 'financial_transactions', $profileId, $rel);
                $tenant->prepare("UPDATE financial_transactions SET is_deleted=1,updated_at=NOW() WHERE id=:id AND profile_id=:pid")->execute([':id' => $rel, ':pid' => $profileId]);
                syncEnqueueGlobalEvent($tenant, $profileId, 'financial_transactions', 'UPDATE', $rel, $finBefore, syncFetchRowSnapshot($tenant, 'financial_transactions', $profileId, $rel), ['source' => 'web.customer_event_delete']);
            }
            
            // Tarihçe kaydı senkronizasyonu
            $histBefore = syncFetchRowSnapshot($tenant, 'customer_history_events', $profileId, $eventId);
            $tenant->prepare("DELETE FROM customer_history_events WHERE id=:id AND profile_id=:pid")->execute([':id' => $eventId, ':pid' => $profileId]);
            syncEnqueueGlobalEvent($tenant, $profileId, 'customer_history_events', 'DELETE', $eventId, $histBefore, null, ['source' => 'web.customer_event_delete']);
            
            $tenant->commit();
            flash('ok', 'Hareket silindi ve tüm cihazlara senkronize edildi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Hareket silinemedi: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'customer_event_update') {
        if (!hasPerm($user, 'perm_accounts')) {
            flash('err', 'Cari hesap yetkiniz yok.');
            go(routeFromReq());
        }
        $eventId = trim((string)($_POST['event_id'] ?? ''));
        $personName = trim((string)($_POST['person_name'] ?? ''));
        $eventType = trim((string)($_POST['event_type'] ?? ''));
        $newDesc = trim((string)($_POST['description'] ?? ''));
        $newCurrency = trim((string)($_POST['currency'] ?? ''));
        $newAmount = parseAmount((string)($_POST['amount'] ?? '0'));
        $eventDate = normDate((string)($_POST['transaction_date'] ?? '')) . ' ' . date('H:i:s');
        $route = routeFromReq();
        if ($personName !== '') {
            $route['person'] = $personName;
        }
        if ($eventId === '') {
            flash('err', 'Guncelleme verisi gecersiz.');
            go($route);
        }
        try {
            $tenant->beginTransaction();
            $st = $tenant->prepare("SELECT * FROM customer_history_events WHERE id=:id AND profile_id=:pid LIMIT 1 FOR UPDATE");
            $st->execute([':id' => $eventId, ':pid' => $profileId]);
            $ev = $st->fetch();
            if (!$ev) {
                throw new RuntimeException('Hareket bulunamadı.');
            }

            // PYTHON MANTIĞI: Orijinal işlemi koru, açıklamaya "[Düzenlendi]" ekle
            $finalDesc = $newDesc !== '' ? $newDesc : (string)($ev['description'] ?? '');
            if (strpos($finalDesc, '[Düzenlendi]') === false) {
                $finalDesc = '[Düzenlendi] ' . $finalDesc;
            }

            $finalCurrency = $newCurrency !== '' ? $newCurrency : (string)($ev['currency'] ?? '');
            $debt = (float)($ev['debt_original'] ?? 0);
            $credit = (float)($ev['credit_original'] ?? 0);

            if ($finalDesc !== '' && substr($finalDesc, 0, 1) === '{') {
                $jd = json_decode($finalDesc, true);
                if (is_array($jd) && $newDesc !== '') {
                    $jd['main_desc'] = $newDesc;
                    $finalDesc = json_encode($jd, JSON_UNESCAPED_UNICODE);
                }
            } else {
                if ($newAmount > 0) {
                    if ($debt > 0) $debt = $newAmount;
                    elseif ($credit > 0) $credit = $newAmount;
                }
            }

            $histBefore = syncFetchRowSnapshot($tenant, 'customer_history_events', $profileId, $eventId);
            // event_type ASLA DEĞİŞMEZ, orijinalliği korunur
            $tenant->prepare("UPDATE customer_history_events SET event_date=:ed, description=:d, debt_original=:debt, credit_original=:credit, currency=:cur WHERE id=:id AND profile_id=:pid")
                ->execute([':ed' => $eventDate, ':d' => $finalDesc, ':debt' => $debt, ':credit' => $credit, ':cur' => $finalCurrency, ':id' => $eventId, ':pid' => $profileId]);
            syncEnqueueGlobalEvent($tenant, $profileId, 'customer_history_events', 'UPDATE', $eventId, $histBefore, syncFetchRowSnapshot($tenant, 'customer_history_events', $profileId, $eventId), ['source' => 'web.customer_event_update']);

            // Eğer bağlı bir kasa işlemi varsa onu da güncelle
            if (!empty($ev['related_id'])) {
                $rel = $ev['related_id'];
                $finBefore = syncFetchRowSnapshot($tenant, 'financial_transactions', $profileId, $rel);
                $tenant->prepare("UPDATE financial_transactions SET amount=:amt, base_amount=:b_amt, currency=:cur, transaction_date=:td WHERE id=:rid AND profile_id=:pid")
                       ->execute([':amt' => $newAmount, ':b_amt' => $newAmount, ':cur' => $finalCurrency, ':td' => normDate((string)($_POST['transaction_date'] ?? '')), ':rid' => $rel, ':pid' => $profileId]);
                syncEnqueueGlobalEvent($tenant, $profileId, 'financial_transactions', 'UPDATE', $rel, $finBefore, syncFetchRowSnapshot($tenant, 'financial_transactions', $profileId, $rel), ['source' => 'web.customer_event_update']);
            }

            $tenant->commit();
            flash('ok', 'Hareket güncellendi ve cihazlara senkronize edildi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Hareket güncellenemedi: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'add_debt_record') {
        if (!hasPerm($user, 'perm_accounts')) {
            flash('err', 'Cari hesap yetkiniz yok.');
            go(routeFromReq());
        }
        $personName = trim((string)($_POST['person_name'] ?? ''));
        $recordType = trim((string)($_POST['record_type'] ?? ''));
        $debtType = trim((string)($_POST['debt_type'] ?? 'Cash'));
        $dateSql = normDate((string)($_POST['transaction_date'] ?? ''));
        $dueRaw = trim((string)($_POST['due_date'] ?? ''));
        $dueDate = ($dueRaw === '') ? null : normDate($dueRaw);
        $description = trim((string)($_POST['description'] ?? ''));
        $route = routeFromReq();
        if ($personName !== '') {
            $route['person'] = $personName;
        }
        if (!in_array($recordType, ['Receivable', 'Payable'], true)) {
            flash('err', 'Kayıt tipi geçersiz.');
            go($route);
        }
        if (!in_array($debtType, ['Cash', 'Product'], true)) {
            flash('err', 'Borç tipi geçersiz.');
            go($route);
        }
        try {
            $personId = personIdByName($tenant, $profileId, $personName);
            if (!$personId) {
                throw new RuntimeException('Müteri bulunamadı.');
            }
            $debtId = uuid4();
            $eventDate = $dateSql . ' ' . date('H:i:s');
            $eventType = ($recordType === 'Receivable') ? 'Alacak' : 'Borç';
            $currency = 'TRY';
            $origAmount = 0.0;
            $totalBase = 0.0;
            $productName = null;
            $productQty = 0.0;
            $histDebt = 0.0;
            $histCredit = 0.0;

            $tenant->beginTransaction();

            if ($debtType === 'Cash') {
                $amount = parseAmount((string)($_POST['amount'] ?? ''));
                $currency = trim((string)($_POST['currency'] ?? 'TRY'));
                if ($amount <= 0) {
                    throw new RuntimeException('Nakit tutarı 0\'dan büyük olmalı.');
                }
                if (!in_array($currency, $SUPPORTED_CURRENCIES, true)) {
                    throw new RuntimeException('Geçersiz para birimi.');
                }
                $origAmount = $amount;
                $totalBase = $amount;
                $histDebt = ($recordType === 'Receivable') ? $amount : 0.0;
                $histCredit = ($recordType === 'Payable') ? $amount : 0.0;

                if (isset($_POST['affect_cash'])) {
                    [$accType, $accId] = parseAccountKey((string)($_POST['source_account_key'] ?? ''));
                    if (!$accType || !$accId || !canAccessAccount($user, $accType, $accId)) {
                        throw new RuntimeException('Kasa/Kart hesabı seçimi geçersiz.');
                    }
                    $acc = getAccount($tenant, $profileId, $accId);
                    if (!$acc) {
                        throw new RuntimeException('Hesap bulunamadı.');
                    }
                    $tenant->prepare("SELECT id FROM financial_accounts WHERE id=:id AND profile_id=:pid FOR UPDATE")
                        ->execute([':id' => $accId, ':pid' => $profileId]);
                    if ($recordType === 'Receivable') {
                        $bal = getBalance($tenant, $profileId, $accId, $currency);
                        if ($bal < $amount) {
                            throw new RuntimeException("Yetersiz bakiye. {$acc['name']} hesabında {$bal} {$currency} var.");
                        }
                    }
                    $finType = ($recordType === 'Receivable') ? 'Expense' : 'Income';
                    $finDesc = ($recordType === 'Receivable')
                        ? "{$personName} kişisine borç verildi."
                        : "{$personName} kişisinden borç alındı.";
                    if ($description !== '') {
                        $finDesc .= " ({$description})";
                    }
                    addFinTrans($tenant, $profileId, $accId, $dateSql, $finType, $amount, $currency, $amount, $finDesc, $personId, $debtId);
                }
            } else {
                $productName = trim((string)($_POST['product_name'] ?? ''));
                $productQty = parseAmount((string)($_POST['product_quantity'] ?? ''));
                $depotName = trim((string)($_POST['depot_name'] ?? ''));
                if ($productName === '' || $productQty <= 0 || $depotName === '') {
                    throw new RuntimeException('Ürün, miktar ve depo zorunludur.');
                }
                $ps = $tenant->prepare("SELECT default_purchase_price FROM product_definitions WHERE profile_id=:pid AND stock_type='Product' AND is_deleted=0 AND name=:n LIMIT 1");
                $ps->execute([':pid' => $profileId, ':n' => $productName]);
                $prow = $ps->fetch();
                $price = (float)($prow['default_purchase_price'] ?? 0);
                $totalBase = $price * $productQty;
                $eventType .= ' (Ürün)';
                $currency = $productName;
                if (isset($_POST['affect_stock'])) {
                    if ($recordType === 'Receivable') {
                        stockRemoveProduct($tenant, $profileId, $depotName, $productName, $productQty);
                    } else {
                        stockAddProduct($tenant, $profileId, $depotName, $productName, $productQty, $price);
                    }
                }
            }

            $tenant->prepare("INSERT INTO debt_records (id,profile_id,person_id,person_full_name,record_type,transaction_date,debt_category,original_amount,currency,total_amount_base,product_name,product_quantity,description,status,due_date,is_paid,paid_amount_base,paid_quantity,payment_date,is_deleted,created_at,updated_at) VALUES (:id,:pid,:person_id,:person_name,:rt,:dt,:dc,:oa,:cur,:tb,:pn,:pq,:desc,'Active',:due,0,0,0,NULL,0,NOW(),NOW())")
                ->execute([
                    ':id' => $debtId,
                    ':pid' => $profileId,
                    ':person_id' => $personId,
                    ':person_name' => $personName,
                    ':rt' => $recordType,
                    ':dt' => $dateSql,
                    ':dc' => $debtType,
                    ':oa' => $origAmount,
                    ':cur' => $currency,
                    ':tb' => $totalBase,
                    ':pn' => $productName,
                    ':pq' => $productQty,
                    ':desc' => $description,
                    ':due' => $dueDate,
                ]);

            addHist(
                $tenant,
                $profileId,
                $personId,
                $personName,
                $eventDate,
                $eventType,
                ($description !== '' ? $description : "{$recordType} kaydı oluşturuldu."),
                $histDebt,
                $histCredit,
                $currency,
                $debtId
            );
            $tenant->commit();
            flash('ok', 'Cari kayıt eklendi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Cari kayıt hatası: ' . $ex->getMessage());
        }
        go($route);
    }

    if ($act === 'quick_payment') {
        flash('err', 'Hizli Tahsilat / Odeme modulu kaldirildi.');
        go(routeFromReq());
    }

    if ($act === 'service_definition_add') {
        if (!$isAdmin && !hasPerm($user, 'perm_services')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $name = trim((string)($_POST['service_def_name'] ?? ''));
        $colsRaw = trim((string)($_POST['service_custom_columns'] ?? ''));
        $cloneColsRaw = trim((string)($_POST['service_clone_columns'] ?? ''));
        if ($name === '') {
            flash('err', 'Hizmet tanimi zorunludur.');
            go(routeFromReq());
        }
        $cols = splitDynamicLabels($colsRaw);
        $cloneCols = splitDynamicLabels($cloneColsRaw);
        try {
            $st = $tenant->prepare("SELECT id FROM service_definitions WHERE profile_id=:pid AND name=:n LIMIT 1");
            $st->execute([':pid' => $profileId, ':n' => $name]);
            if ($st->fetch()) {
                throw new RuntimeException('Ayni adla tanim zaten var.');
            }
            $newServiceDefId = uuid4();
            $tenant->prepare("INSERT INTO service_definitions (id,profile_id,name,custom_columns_json,clone_columns_json) VALUES (:id,:pid,:n,:c,:cc)")
                ->execute([
                    ':id' => $newServiceDefId,
                    ':pid' => $profileId,
                    ':n' => $name,
                    ':c' => json_encode($cols, JSON_UNESCAPED_UNICODE),
                    ':cc' => json_encode($cloneCols, JSON_UNESCAPED_UNICODE),
                ]);
            $afterRow = syncFetchRowSnapshot($tenant, 'service_definitions', $profileId, $newServiceDefId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'service_definitions',
                'ADD',
                $newServiceDefId,
                null,
                $afterRow,
                ['source' => 'web.service_definition_add']
            );
            flash('ok', 'Hizmet tanimi eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Hizmet tanimi eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'service_definition_delete') {
        if (!$isAdmin && !hasPerm($user, 'perm_services')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $defId = trim((string)($_POST['service_def_id'] ?? ''));
        if ($defId === '') {
            flash('err', 'Silinecek tanim secilmedi.');
            go(routeFromReq());
        }
        try {
            $beforeDefRow = syncFetchRowSnapshot($tenant, 'service_definitions', $profileId, $defId);
            $jobRows = $tenant->prepare("SELECT id FROM service_jobs WHERE profile_id=:pid AND service_def_id=:id LIMIT 1");
            $jobRows->execute([':pid' => $profileId, ':id' => $defId]);
            if ($jobRows->fetch()) {
                throw new RuntimeException('Bu hizmet tanimina bagli kayitlar var.');
            }
            $tenant->prepare("DELETE FROM service_definitions WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $defId, ':pid' => $profileId]);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'service_definitions',
                'DELETE',
                $defId,
                $beforeDefRow,
                null,
                ['source' => 'web.service_definition_delete']
            );
            flash('ok', 'Hizmet tanimi silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Silme hatasi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'service_job_add') {
        if (!$isAdmin && !hasPerm($user, 'perm_services')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $defId = trim((string)($_POST['service_def_id'] ?? ''));
        $customerName = trim((string)($_POST['customer_name'] ?? ''));
        $desc = trim((string)($_POST['description'] ?? ''));
        $status = trim((string)($_POST['status'] ?? 'Alindi'));
        $fee = parseAmount((string)($_POST['fee'] ?? '0'));
        $feeCurrency = strtoupper(trim((string)($_POST['fee_currency'] ?? 'TRY')));
        $startDate = normDate((string)($_POST['start_date'] ?? ''));
        $deliveryDateRaw = trim((string)($_POST['delivery_date'] ?? ''));
        $deliveryDate = $deliveryDateRaw === '' ? null : normDate($deliveryDateRaw);
        $dynamicPayload = serviceBuildDynamicPayloadFromRequest();
        if ($defId === '' || $customerName === '') {
            flash('err', 'Hizmet tipi ve musteri zorunludur.');
            go(routeFromReq());
        }
        if (!in_array($feeCurrency, $SUPPORTED_CURRENCIES, true)) {
            $feeCurrency = 'TRY';
        }
        try {
            $newServiceJobId = uuid4();
            $tenant->prepare("INSERT INTO service_jobs (id,profile_id,service_def_id,customer_name,description,status,fee,currency,start_date,delivery_date,dynamic_data_json) VALUES (:id,:pid,:sd,:cn,:d,:s,:f,:c,:st,:dt,:j)")
                ->execute([
                    ':id' => $newServiceJobId,
                    ':pid' => $profileId,
                    ':sd' => $defId,
                    ':cn' => $customerName,
                    ':d' => $desc,
                    ':s' => ($status === '' ? 'Alindi' : $status),
                    ':f' => $fee,
                    ':c' => $feeCurrency,
                    ':st' => $startDate,
                    ':dt' => $deliveryDate,
                    ':j' => json_encode($dynamicPayload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
                ]);
            $afterRow = syncFetchRowSnapshot($tenant, 'service_jobs', $profileId, $newServiceJobId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'service_jobs',
                'ADD',
                $newServiceJobId,
                null,
                $afterRow,
                ['source' => 'web.service_job_add']
            );
            flash('ok', 'Hizmet kaydi eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Hizmet kaydi eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'advanced_collect_and_deliver') {
        if (!$isAdmin && !hasAnyPerm($user, ['perm_services', 'perm_repair'])) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }

        $moduleType = trim((string)($_POST['module_type'] ?? 'service'));
        $recordId = trim((string)($_POST['record_id'] ?? ''));
        $paymentNote = trim((string)($_POST['payment_note'] ?? ''));
        $payAccounts = $_POST['service_pay_account'] ?? [];
        $payAmounts = $_POST['service_pay_amount'] ?? [];
        $targetCurrency = trim((string)($_POST['paid_currency'] ?? 'TRY'));

        if ($recordId === '') {
            flash('err', 'Geçersiz kayıt.');
            go(routeFromReq());
        }

        try {
            $tenant->beginTransaction();
            $personName = '';
            $totalPaid = 0.0;
            $serviceName = '';

            if ($moduleType === 'service') {
                $stJob = $tenant->prepare("SELECT j.*, COALESCE(d.name,'-') as sname FROM service_jobs j LEFT JOIN service_definitions d ON d.id=j.service_def_id WHERE j.id=:id AND j.profile_id=:pid LIMIT 1 FOR UPDATE");
                $stJob->execute([':id' => $recordId, ':pid' => $profileId]);
                $row = $stJob->fetch();
                if (!$row) {
                    throw new RuntimeException('Hizmet kaydı bulunamadı.');
                }

                $personName = $row['customer_name'];
                $serviceName = $row['sname'];
                $tenant->prepare("UPDATE service_jobs SET status='Teslim Edildi', delivery_date=UTC_DATE() WHERE id=:id")
                    ->execute([':id' => $recordId]);
            } else {
                $stRep = $tenant->prepare("SELECT * FROM repair_records WHERE id=:id AND profile_id=:pid LIMIT 1 FOR UPDATE");
                $stRep->execute([':id' => $recordId, ':pid' => $profileId]);
                $row = $stRep->fetch();
                if (!$row) {
                    throw new RuntimeException('Tamir kaydı bulunamadı.');
                }

                $personName = $row['customer_full_name'];
                $serviceName = "Tamir: " . $row['product_description'];
                $tenant->prepare("UPDATE repair_records SET status='Teslim Edildi', delivery_date=UTC_DATE() WHERE id=:id")
                    ->execute([':id' => $recordId]);
            }

            if (is_array($payAccounts) && is_array($payAmounts)) {
                foreach ($payAccounts as $idx => $accStr) {
                    $amt = parseAmount((string)($payAmounts[$idx] ?? '0'));
                    if ($amt <= 0) {
                        continue;
                    }

                    [$accType, $accId] = parseAccountKey($accStr);
                    if ($accType && $accId) {
                        $desc = ($paymentNote !== '') ? $paymentNote : "Tahsilat ($serviceName) - $personName";
                        addFinTrans($tenant, $profileId, $accId, date('Y-m-d'), 'Income', $amt, $targetCurrency, $amt, $desc, null, $recordId);
                        $totalPaid += $amt;
                    }
                }
            }

            if ($personName !== '' && $totalPaid > 0) {
                $personId = personIdByName($tenant, $profileId, $personName);
                $historyDesc = ($paymentNote !== '') ? $paymentNote : "Tahsilat tamamlandı ($serviceName)";
                addHist($tenant, $profileId, $personId, $personName, date('Y-m-d H:i:s'), 'TAHSİLAT (HİZMET/TAMİR)', $historyDesc, 0.0, $totalPaid, $targetCurrency, $recordId);
            }

            $tenant->commit();
            flash('ok', 'İşlem teslim edildi ve kasaya tahsilat eklendi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'İşlem hatası: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'service_job_status') {
        if (!$isAdmin && !hasPerm($user, 'perm_services')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $jobId = trim((string)($_POST['service_job_id'] ?? ''));
        $status = trim((string)($_POST['status'] ?? ''));
        if ($jobId === '' || $status === '') {
            flash('err', 'Kayit ve durum zorunludur.');
            go(routeFromReq());
        }
        try {
            $deliveryDate = ($status === 'Teslim Edildi') ? date('Y-m-d') : null;
            $beforeRow = syncFetchRowSnapshot($tenant, 'service_jobs', $profileId, $jobId);
            $tenant->prepare("UPDATE service_jobs SET status=:s, delivery_date=:d WHERE id=:id AND profile_id=:pid")
                ->execute([':s' => $status, ':d' => $deliveryDate, ':id' => $jobId, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'service_jobs', $profileId, $jobId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'service_jobs',
                'UPDATE',
                $jobId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.service_job_status']
            );
            flash('ok', 'Hizmet durumu guncellendi.');
        } catch (Throwable $ex) {
            flash('err', 'Durum guncellenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'service_job_delete') {
        if (!$isAdmin && !hasPerm($user, 'perm_services')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $jobId = trim((string)($_POST['service_job_id'] ?? ''));
        if ($jobId === '') {
            flash('err', 'Silinecek kayit secilmedi.');
            go(routeFromReq());
        }
        try {
            $beforeRow = syncFetchRowSnapshot($tenant, 'service_jobs', $profileId, $jobId);
            $tenant->prepare("DELETE FROM service_jobs WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $jobId, ':pid' => $profileId]);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'service_jobs',
                'DELETE',
                $jobId,
                $beforeRow,
                null,
                ['source' => 'web.service_job_delete']
            );
            flash('ok', 'Hizmet kaydi silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Silme hatasi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'repair_add') {
        if (!$isAdmin && !hasPerm($user, 'perm_repair')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $customerName = trim((string)($_POST['customer_name'] ?? ''));
        $productDesc = trim((string)($_POST['product_description'] ?? ''));
        $complaint = trim((string)($_POST['complaint_request'] ?? ''));
        $receiptDate = normDate((string)($_POST['receipt_date'] ?? ''));
        $fee = parseAmount((string)($_POST['estimated_fee'] ?? '0'));
        $feeCurrency = strtoupper(trim((string)($_POST['fee_currency'] ?? 'TRY')));
        $status = trim((string)($_POST['status'] ?? 'Alindi'));
        $notes = trim((string)($_POST['notes'] ?? ''));
        if ($customerName === '' || $productDesc === '') {
            flash('err', 'Musteri ve urun aciklamasi zorunludur.');
            go(routeFromReq());
        }
        if (!in_array($feeCurrency, $SUPPORTED_CURRENCIES, true)) {
            $feeCurrency = 'TRY';
        }
        try {
            $newRepairId = uuid4();
            $tenant->prepare("INSERT INTO repair_records (id,profile_id,customer_full_name,product_description,complaint_request,receipt_date,estimated_fee,fee_currency,status,delivery_date,notes,is_deleted,created_at,updated_at) VALUES (:id,:pid,:c,:p,:r,:rd,:f,:fc,:s,NULL,:n,0,NOW(),NOW())")
                ->execute([
                    ':id' => $newRepairId,
                    ':pid' => $profileId,
                    ':c' => $customerName,
                    ':p' => $productDesc,
                    ':r' => $complaint,
                    ':rd' => $receiptDate,
                    ':f' => $fee,
                    ':fc' => $feeCurrency,
                    ':s' => ($status === '' ? 'Alindi' : $status),
                    ':n' => $notes,
                ]);
            $afterRow = syncFetchRowSnapshot($tenant, 'repair_records', $profileId, $newRepairId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'repair_records',
                'ADD',
                $newRepairId,
                null,
                $afterRow,
                ['source' => 'web.repair_add']
            );
            flash('ok', 'Tamir kaydi eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Tamir kaydi eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'repair_status') {
        if (!$isAdmin && !hasPerm($user, 'perm_repair')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $repairId = trim((string)($_POST['repair_id'] ?? ''));
        $status = trim((string)($_POST['status'] ?? ''));
        if ($repairId === '' || $status === '') {
            flash('err', 'Kayit ve durum zorunludur.');
            go(routeFromReq());
        }
        try {
            $deliveryDate = ($status === 'Teslim Edildi') ? date('Y-m-d') : null;
            $beforeRow = syncFetchRowSnapshot($tenant, 'repair_records', $profileId, $repairId);
            $tenant->prepare("UPDATE repair_records SET status=:s, delivery_date=:d, updated_at=NOW() WHERE id=:id AND profile_id=:pid")
                ->execute([':s' => $status, ':d' => $deliveryDate, ':id' => $repairId, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'repair_records', $profileId, $repairId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'repair_records',
                'UPDATE',
                $repairId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.repair_status']
            );
            flash('ok', 'Tamir durumu guncellendi.');
        } catch (Throwable $ex) {
            flash('err', 'Durum guncellenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'repair_delete') {
        if (!$isAdmin && !hasPerm($user, 'perm_repair')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $repairId = trim((string)($_POST['repair_id'] ?? ''));
        if ($repairId === '') {
            flash('err', 'Silinecek kayit secilmedi.');
            go(routeFromReq());
        }
        try {
            $beforeRow = syncFetchRowSnapshot($tenant, 'repair_records', $profileId, $repairId);
            $tenant->prepare("UPDATE repair_records SET is_deleted=1, updated_at=NOW() WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $repairId, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'repair_records', $profileId, $repairId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'repair_records',
                'UPDATE',
                $repairId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.repair_delete']
            );
            flash('ok', 'Tamir kaydi silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Silme hatasi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'agenda_save') {
        if (!$isAdmin && !hasPerm($user, 'perm_agenda')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $noteDate = trim((string)($_POST['agenda_date'] ?? date('Y-m-d')));
        $noteTitle = trim((string)($_POST['agenda_title'] ?? ''));
        $noteTime = trim((string)($_POST['agenda_time'] ?? ''));
        $noteText = trim((string)($_POST['agenda_note'] ?? ''));
        $isReminder = !empty($_POST['agenda_is_reminder']);
        if ($noteDate === '') {
            $noteDate = date('Y-m-d');
        }
        try {
            if ($noteTitle === '' && $noteTime === '' && $noteText === '') {
                $tenant->prepare("DELETE FROM agenda_notes WHERE profile_id=:pid AND note_key=:k")
                    ->execute([':pid' => $profileId, ':k' => $noteDate]);
            } else {
                $tenant->prepare("INSERT INTO agenda_notes (profile_id,note_key,note_value) VALUES (:pid,:k,:v) ON DUPLICATE KEY UPDATE note_value=VALUES(note_value)")
                    ->execute([
                        ':pid' => $profileId,
                        ':k' => $noteDate,
                        ':v' => agendaEncodeNote($noteDate, $noteTitle, $noteTime, $noteText, $isReminder),
                    ]);
            }
            flash('ok', 'Ajanda notu kaydedildi.');
        } catch (Throwable $ex) {
            flash('err', 'Ajanda notu kaydedilemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'agenda_delete') {
        if (!$isAdmin && !hasPerm($user, 'perm_agenda')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $noteDate = trim((string)($_POST['agenda_date'] ?? ''));
        if ($noteDate === '') {
            flash('err', 'Silinecek tarih secilmedi.');
            go(routeFromReq());
        }
        try {
            $tenant->prepare("DELETE FROM agenda_notes WHERE profile_id=:pid AND note_key=:k")
                ->execute([':pid' => $profileId, ':k' => $noteDate]);
            flash('ok', 'Ajanda notu silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Ajanda notu silinemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'report_export') {
        if (!$isAdmin && !hasPerm($user, 'perm_reports')) {
            flash('err', 'Yetkiniz yok.');
            go(routeFromReq());
        }
        $format = strtolower(trim((string)($_POST['export_format'] ?? 'excel')));
        $reportType = trim((string)($_POST['report_type'] ?? 'cashflow'));
        if ($reportType === 'stock') {
            $reportType = 'stock_product';
        }
        $reportStart = normDate((string)($_POST['report_start'] ?? date('Y-m-01')));
        $reportEnd = normDate((string)($_POST['report_end'] ?? date('Y-m-d')));
        $reportCustomer = trim((string)($_POST['report_customer'] ?? ''));
        $reportDepot = trim((string)($_POST['report_depot'] ?? ''));
        if (!in_array($format, ['excel', 'pdf'], true)) {
            flash('err', 'Export formati gecersiz.');
            go(routeFromReq());
        }
        if (!in_array($reportType, ['cashflow', 'profit', 'stock_product', 'stock_scrap', 'stock_ziynet', 'customer_statement', 'service_general', 'tax'], true)) {
            $reportType = 'cashflow';
        }
        try {
            $allowedDepots = [];
            foreach (fetchDepots($tenant, $profileId, $user) as $depRow) {
                $name = trim((string)($depRow['name'] ?? ''));
                if ($name !== '') {
                    $allowedDepots[] = $name;
                }
            }
            $dataset = buildReportDataset(
                $tenant,
                $profileId,
                $reportType,
                $reportStart,
                $reportEnd,
                $SUPPORTED_CURRENCIES,
                $reportCustomer,
                $reportDepot,
                $allowedDepots
            );
            if (!loadComposerAutoload()) {
                throw new RuntimeException('Composer autoload bulunamadi. web/v.1.1 dizininde "composer install" calistirin.');
            }
            $title = (string)($dataset['title'] ?? 'Rapor');
            $headers = is_array($dataset['headers'] ?? null) ? $dataset['headers'] : [];
            $rows = is_array($dataset['rows'] ?? null) ? $dataset['rows'] : [];
            $summary = is_array($dataset['summary'] ?? null) ? $dataset['summary'] : [];
            if ($format === 'excel') {
                outputReportExcel($title, $headers, $rows, $summary);
            }
            outputReportPdf($title, $headers, $rows, $summary);
        } catch (Throwable $ex) {
            flash('err', 'Rapor export hatasi: ' . $ex->getMessage());
            $route = routeFromReq();
            if (!isset($route['cat'])) {
                $route['cat'] = 'tools';
            }
            if (!isset($route['sub'])) {
                $route['sub'] = 'tools-reports';
            }
            go($route);
        }
    }

    if ($act === 'tax_definition_add' || $act === 'tax_definition_update' || $act === 'tax_definition_delete') {
        if (!$isAdmin && !hasPerm($user, 'perm_accounts')) {
            flash('err', 'Bu islem icin yetkiniz yok.');
            go(routeFromReq());
        }
        try {
            if ($act === 'tax_definition_delete') {
                $taxId = trim((string)($_POST['tax_id'] ?? ''));
                if ($taxId === '') {
                    throw new RuntimeException('Silinecek vergi secilmedi.');
                }
                $beforeRow = syncFetchRowSnapshot($tenant, 'taxes', $profileId, $taxId);
                $tenant->prepare("DELETE FROM taxes WHERE id=:id AND profile_id=:pid")
                    ->execute([':id' => $taxId, ':pid' => $profileId]);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'taxes',
                    'DELETE',
                    $taxId,
                    $beforeRow,
                    null,
                    ['source' => 'web.tax_definition_delete']
                );
                flash('ok', 'Vergi tanimi silindi.');
                go(routeFromReq());
            }

            $taxName = trim((string)($_POST['tax_name'] ?? ''));
            $taxType = strtolower(trim((string)($_POST['tax_type'] ?? 'percent')));
            $taxRate = parseAmount((string)($_POST['tax_rate'] ?? '0'));
            $taxCountry = strtoupper(trim((string)($_POST['tax_country'] ?? '')));
            $taxActive = !empty($_POST['tax_active']) ? 1 : 0;
            if ($taxName === '') {
                throw new RuntimeException('Vergi adi zorunludur.');
            }
            if (!in_array($taxType, ['percent', 'fixed'], true)) {
                $taxType = 'percent';
            }
            if ($taxRate < 0) {
                $taxRate = 0.0;
            }
            if (strlen($taxCountry) > 2) {
                $taxCountry = substr($taxCountry, 0, 2);
            }
            if ($taxCountry === '') {
                $taxCountry = null;
            }

            if ($act === 'tax_definition_add') {
                $newTaxId = uuid4();
                $tenant->prepare(
                    "INSERT INTO taxes (id,profile_id,name,rate,type,country,active,created_at,updated_at)
                     VALUES (:id,:pid,:n,:r,:t,:c,:a,NOW(),NOW())"
                )->execute([
                    ':id' => $newTaxId,
                    ':pid' => $profileId,
                    ':n' => $taxName,
                    ':r' => $taxRate,
                    ':t' => $taxType,
                    ':c' => $taxCountry,
                    ':a' => $taxActive,
                ]);
                $afterRow = syncFetchRowSnapshot($tenant, 'taxes', $profileId, $newTaxId);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'taxes',
                    'ADD',
                    $newTaxId,
                    null,
                    $afterRow,
                    ['source' => 'web.tax_definition_add']
                );
                flash('ok', 'Vergi tanimi eklendi.');
                go(routeFromReq());
            }

            $taxId = trim((string)($_POST['tax_id'] ?? ''));
            if ($taxId === '') {
                throw new RuntimeException('Duzenlenecek vergi secilmedi.');
            }
            $beforeRow = syncFetchRowSnapshot($tenant, 'taxes', $profileId, $taxId);
            $tenant->prepare(
                "UPDATE taxes
                 SET name=:n, rate=:r, type=:t, country=:c, active=:a, updated_at=NOW()
                 WHERE id=:id AND profile_id=:pid"
            )->execute([
                ':n' => $taxName,
                ':r' => $taxRate,
                ':t' => $taxType,
                ':c' => $taxCountry,
                ':a' => $taxActive,
                ':id' => $taxId,
                ':pid' => $profileId,
            ]);
            $afterRow = syncFetchRowSnapshot($tenant, 'taxes', $profileId, $taxId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'taxes',
                'UPDATE',
                $taxId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.tax_definition_update']
            );
            flash('ok', 'Vergi tanimi guncellendi.');
        } catch (Throwable $ex) {
            flash('err', 'Vergi tanimi islemi basarisiz: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'tax_profile_add' || $act === 'tax_profile_update' || $act === 'tax_profile_delete') {
        if (!$isAdmin && !hasPerm($user, 'perm_accounts')) {
            flash('err', 'Bu islem icin yetkiniz yok.');
            go(routeFromReq());
        }
        try {
            if ($act === 'tax_profile_delete') {
                $profileTaxId = trim((string)($_POST['tax_profile_id'] ?? ''));
                if ($profileTaxId === '') {
                    throw new RuntimeException('Silinecek vergi profili secilmedi.');
                }
                $itemSt = $tenant->prepare("SELECT id FROM tax_profile_items WHERE profile_id=:pid AND tax_profile_id=:tpid");
                $itemSt->execute([':pid' => $profileId, ':tpid' => $profileTaxId]);
                foreach ($itemSt->fetchAll() ?: [] as $itemRow) {
                    $itemId = trim((string)($itemRow['id'] ?? ''));
                    if ($itemId === '') {
                        continue;
                    }
                    $beforeItemRow = syncFetchRowSnapshot($tenant, 'tax_profile_items', $profileId, $itemId);
                    $tenant->prepare("DELETE FROM tax_profile_items WHERE id=:id AND profile_id=:pid")
                        ->execute([':id' => $itemId, ':pid' => $profileId]);
                    syncEnqueueGlobalEvent(
                        $tenant,
                        $profileId,
                        'tax_profile_items',
                        'DELETE',
                        $itemId,
                        $beforeItemRow,
                        null,
                        ['source' => 'web.tax_profile_delete']
                    );
                }
                $beforeRow = syncFetchRowSnapshot($tenant, 'tax_profiles', $profileId, $profileTaxId);
                $tenant->prepare("DELETE FROM tax_profiles WHERE id=:id AND profile_id=:pid")
                    ->execute([':id' => $profileTaxId, ':pid' => $profileId]);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'tax_profiles',
                    'DELETE',
                    $profileTaxId,
                    $beforeRow,
                    null,
                    ['source' => 'web.tax_profile_delete']
                );
                flash('ok', 'Vergi profili silindi.');
                go(routeFromReq());
            }

            $name = trim((string)($_POST['tax_profile_name'] ?? ''));
            $desc = trim((string)($_POST['tax_profile_description'] ?? ''));
            if ($name === '') {
                throw new RuntimeException('Vergi profili adi zorunludur.');
            }
            if ($act === 'tax_profile_add') {
                $newId = uuid4();
                $tenant->prepare(
                    "INSERT INTO tax_profiles (id,profile_id,name,description,created_at,updated_at)
                     VALUES (:id,:pid,:n,:d,NOW(),NOW())"
                )->execute([':id' => $newId, ':pid' => $profileId, ':n' => $name, ':d' => $desc]);
                $afterRow = syncFetchRowSnapshot($tenant, 'tax_profiles', $profileId, $newId);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'tax_profiles',
                    'ADD',
                    $newId,
                    null,
                    $afterRow,
                    ['source' => 'web.tax_profile_add']
                );
                flash('ok', 'Vergi profili eklendi.');
                go(routeFromReq());
            }
            $profileTaxId = trim((string)($_POST['tax_profile_id'] ?? ''));
            if ($profileTaxId === '') {
                throw new RuntimeException('Duzenlenecek vergi profili secilmedi.');
            }
            $beforeRow = syncFetchRowSnapshot($tenant, 'tax_profiles', $profileId, $profileTaxId);
            $tenant->prepare(
                "UPDATE tax_profiles
                 SET name=:n, description=:d, updated_at=NOW()
                 WHERE id=:id AND profile_id=:pid"
            )->execute([':n' => $name, ':d' => $desc, ':id' => $profileTaxId, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'tax_profiles', $profileId, $profileTaxId);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'tax_profiles',
                'UPDATE',
                $profileTaxId,
                $beforeRow,
                $afterRow,
                ['source' => 'web.tax_profile_update']
            );
            flash('ok', 'Vergi profili guncellendi.');
        } catch (Throwable $ex) {
            flash('err', 'Vergi profili islemi basarisiz: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'tax_profile_items_save') {
        if (!$isAdmin && !hasPerm($user, 'perm_accounts')) {
            flash('err', 'Bu islem icin yetkiniz yok.');
            go(routeFromReq());
        }
        $profileTaxId = trim((string)($_POST['tax_profile_id'] ?? ''));
        if ($profileTaxId === '') {
            flash('err', 'Vergi profili secilmedi.');
            go(routeFromReq());
        }
        $selectedTaxIdsRaw = $_POST['profile_tax_id'] ?? [];
        $priorityMap = $_POST['profile_tax_priority'] ?? [];
        $enabledMap = $_POST['profile_tax_enabled'] ?? [];
        $selectedTaxIds = [];
        if (is_array($selectedTaxIdsRaw)) {
            foreach ($selectedTaxIdsRaw as $tid) {
                $taxId = trim((string)$tid);
                if ($taxId !== '') {
                    $selectedTaxIds[$taxId] = true;
                }
            }
        }
        try {
            $existingSt = $tenant->prepare(
                "SELECT id,tax_id,priority,enabled_by_default
                 FROM tax_profile_items
                 WHERE profile_id=:pid AND tax_profile_id=:tpid"
            );
            $existingSt->execute([':pid' => $profileId, ':tpid' => $profileTaxId]);
            $existingRows = $existingSt->fetchAll() ?: [];
            $existingByTax = [];
            foreach ($existingRows as $row) {
                $taxId = trim((string)($row['tax_id'] ?? ''));
                if ($taxId !== '') {
                    $existingByTax[$taxId] = $row;
                }
            }

            foreach ($existingByTax as $taxId => $row) {
                if (isset($selectedTaxIds[$taxId])) {
                    continue;
                }
                $itemId = trim((string)($row['id'] ?? ''));
                if ($itemId === '') {
                    continue;
                }
                $beforeRow = syncFetchRowSnapshot($tenant, 'tax_profile_items', $profileId, $itemId);
                $tenant->prepare("DELETE FROM tax_profile_items WHERE id=:id AND profile_id=:pid")
                    ->execute([':id' => $itemId, ':pid' => $profileId]);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'tax_profile_items',
                    'DELETE',
                    $itemId,
                    $beforeRow,
                    null,
                    ['source' => 'web.tax_profile_items_save']
                );
            }

            foreach (array_keys($selectedTaxIds) as $taxId) {
                $priority = 0;
                if (is_array($priorityMap) && array_key_exists($taxId, $priorityMap)) {
                    $priority = (int)$priorityMap[$taxId];
                }
                $enabled = 0;
                if (is_array($enabledMap) && array_key_exists($taxId, $enabledMap)) {
                    $enabled = 1;
                }
                if (isset($existingByTax[$taxId])) {
                    $row = $existingByTax[$taxId];
                    $itemId = trim((string)($row['id'] ?? ''));
                    if ($itemId === '') {
                        continue;
                    }
                    $beforeRow = syncFetchRowSnapshot($tenant, 'tax_profile_items', $profileId, $itemId);
                    $tenant->prepare(
                        "UPDATE tax_profile_items
                         SET priority=:pr, enabled_by_default=:en, updated_at=NOW()
                         WHERE id=:id AND profile_id=:pid"
                    )->execute([':pr' => $priority, ':en' => $enabled, ':id' => $itemId, ':pid' => $profileId]);
                    $afterRow = syncFetchRowSnapshot($tenant, 'tax_profile_items', $profileId, $itemId);
                    syncEnqueueGlobalEvent(
                        $tenant,
                        $profileId,
                        'tax_profile_items',
                        'UPDATE',
                        $itemId,
                        $beforeRow,
                        $afterRow,
                        ['source' => 'web.tax_profile_items_save']
                    );
                    continue;
                }
                $newId = uuid4();
                $tenant->prepare(
                    "INSERT INTO tax_profile_items (id,profile_id,tax_profile_id,tax_id,priority,enabled_by_default,created_at,updated_at)
                     VALUES (:id,:pid,:tpid,:tid,:pr,:en,NOW(),NOW())"
                )->execute([
                    ':id' => $newId,
                    ':pid' => $profileId,
                    ':tpid' => $profileTaxId,
                    ':tid' => $taxId,
                    ':pr' => $priority,
                    ':en' => $enabled,
                ]);
                $afterRow = syncFetchRowSnapshot($tenant, 'tax_profile_items', $profileId, $newId);
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'tax_profile_items',
                    'ADD',
                    $newId,
                    null,
                    $afterRow,
                    ['source' => 'web.tax_profile_items_save']
                );
            }
            flash('ok', 'Vergi profili kalemleri guncellendi.');
        } catch (Throwable $ex) {
            flash('err', 'Vergi profili kalemleri guncellenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'settings_general_save') {
        if (!$isAdmin && !hasPerm($user, 'perm_accounts')) {
            flash('err', 'Bu islem icin yetkiniz yok.');
            go(routeFromReq());
        }
        try {
            $keys = [
                'document_prefix', 'shop_name', 'shop_phone',
                'company_name', 'company_address', 'tax_office', 'tax_number',
                'company_phone', 'company_email', 'display_currency', 'language'
            ];
            foreach ($keys as $k) {
                $val = trim((string)($_POST[$k] ?? ''));
                if ($k === 'display_currency' && !in_array(strtoupper($val), $SUPPORTED_CURRENCIES, true)) {
                    continue;
                }
                if ($k === 'language') {
                    $val = web_lang_normalize_code($val);
                }
                settingUpsert($tenant, $profileId, $k, $val, 'str');
            }
            $langCookieValue = web_lang_normalize_code((string)($_POST['language'] ?? web_lang_code()));
            setcookie('golder_lang', $langCookieValue, time() + (86400 * 3650), '/', '', false, false);

            $builtinThemes = themeCatalogBuiltin();
            $customThemes = customThemesFromDb($tenant, $profileId);
            $selectedTheme = trim((string)($_POST['theme_name'] ?? 'Green Harmony'));

            $newThemeName = trim((string)($_POST['new_theme_name'] ?? ''));
            if ($newThemeName !== '') {
                if (isset($builtinThemes[$newThemeName])) {
                    throw new RuntimeException('Bu tema adi yerlesik temalarla ayni olamaz.');
                }
                $newThemeRaw = [
                    'primary' => trim((string)($_POST['new_theme_primary'] ?? '')),
                    'secondary' => trim((string)($_POST['new_theme_secondary'] ?? '')),
                    'background' => trim((string)($_POST['new_theme_background'] ?? '')),
                    'text_dark' => trim((string)($_POST['new_theme_text_dark'] ?? '')),
                    'text_light' => trim((string)($_POST['new_theme_text_light'] ?? '')),
                    'danger' => trim((string)($_POST['new_theme_danger'] ?? '')),
                    'warning' => trim((string)($_POST['new_theme_warning'] ?? '')),
                ];
                $normalizedNewTheme = normalizeThemeDefinition($newThemeRaw);
                if ($normalizedNewTheme === null) {
                    throw new RuntimeException('Yeni tema renkleri gecersiz. Tum renkler #RRGGBB formatinda olmalidir.');
                }
                $customThemes[$newThemeName] = $normalizedNewTheme;
                $selectedTheme = $newThemeName;
            }

            if (!isset($builtinThemes[$selectedTheme]) && !isset($customThemes[$selectedTheme])) {
                $selectedTheme = 'Green Harmony';
            }
            settingUpsert($tenant, $profileId, 'theme_name', $selectedTheme, 'str');
            $customThemesJson = json_encode($customThemes, JSON_UNESCAPED_UNICODE);
            if (!is_string($customThemesJson)) {
                throw new RuntimeException('Tema verisi JSON olarak kaydedilemedi.');
            }
            settingUpsert($tenant, $profileId, 'custom_themes_json', $customThemesJson, 'str');

            flash('ok', 'Genel ayarlar kaydedildi.');
        } catch (Throwable $ex) {
            flash('err', 'Ayarlar kaydedilemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'settings_security_save') {
        if (!$isAdmin) {
            flash('err', 'Bu islem sadece yonetici icindir.');
            go(routeFromReq());
        }
        $newPass = trim((string)($_POST['new_admin_password'] ?? ''));
        $newPass2 = trim((string)($_POST['new_admin_password_repeat'] ?? ''));
        try {
            if ($newPass === '' && $newPass2 === '') {
                ensureDefaultAdmin($tenant, $profileId);
                settingUpsert($tenant, $profileId, 'password_enabled', '0', 'bool');
                settingUpsert($tenant, $profileId, 'password_hash', '', 'str');
                if (!hasActiveNonAdminUsers($tenant, $profileId) && countActiveUsers($tenant, $profileId) === 1) {
                    $adminRowSt = $tenant->prepare("SELECT id FROM users WHERE profile_id=:pid AND role='admin' AND is_deleted=0");
                    $adminRowSt->execute([':pid' => $profileId]);
                    foreach ($adminRowSt->fetchAll() ?: [] as $row) {
                        $adminId = trim((string)($row['id'] ?? ''));
                        if ($adminId === '') {
                            continue;
                        }
                        $beforeRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $adminId);
                        if (!is_array($beforeRow) || trim((string)($beforeRow['password_hash'] ?? '')) === '') {
                            continue;
                        }
                        $tenant->prepare("UPDATE users SET password_hash='' WHERE id=:id AND profile_id=:pid")->execute([':id' => $adminId, ':pid' => $profileId]);
                        $afterRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $adminId);
                        syncEnqueueGlobalEvent(
                            $tenant,
                            $profileId,
                            'users',
                            'UPDATE',
                            $adminId,
                            $beforeRow,
                            $afterRow,
                            ['source' => 'web.settings_security_save.clear_password']
                        );
                    }
                }
                flash('ok', 'Profil sifresi pasif yapildi ve temizlendi.');
            } else {
                if ($newPass === '' || $newPass !== $newPass2) {
                    throw new RuntimeException('Yeni sifreler eslesmiyor.');
                }
                $hash = pbkdfHash($newPass);
                $adminIds = [];
                $adminBeforeRows = [];
                $adminRowSt = $tenant->prepare("SELECT id FROM users WHERE profile_id=:pid AND role='admin' AND is_deleted=0");
                $adminRowSt->execute([':pid' => $profileId]);
                foreach ($adminRowSt->fetchAll() ?: [] as $row) {
                    $adminId = trim((string)($row['id'] ?? ''));
                    if ($adminId === '') {
                        continue;
                    }
                    $adminIds[] = $adminId;
                    $adminBeforeRows[$adminId] = syncFetchRowSnapshot($tenant, 'users', $profileId, $adminId);
                }
                settingUpsert($tenant, $profileId, 'password_enabled', '1', 'bool');
                settingUpsert($tenant, $profileId, 'password_hash', $hash, 'str');
                $tenant->prepare("UPDATE users SET password_hash=:h WHERE profile_id=:pid AND role='admin' AND is_deleted=0")
                    ->execute([':h' => $hash, ':pid' => $profileId]);
                foreach ($adminIds as $adminId) {
                    $afterRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $adminId);
                    syncEnqueueGlobalEvent(
                        $tenant,
                        $profileId,
                        'users',
                        'UPDATE',
                        $adminId,
                        $adminBeforeRows[$adminId] ?? null,
                        $afterRow,
                        ['source' => 'web.settings_security_save']
                    );
                }
                flash('ok', 'Profil sifresi aktif edildi ve guncellendi.');
            }
        } catch (Throwable $ex) {
            flash('err', 'Guvenlik ayarlari kaydedilemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'settings_customer_password_change') {
        if (!$isAdmin) {
            flash('err', 'Bu islem sadece yonetici icindir.');
            go(routeFromReq());
        }
        $customerId = trim((string)($_SESSION['customer']['id'] ?? ''));
        $currentPass = (string)($_POST['current_customer_password'] ?? '');
        $newPass = (string)($_POST['new_customer_password'] ?? '');
        $newPass2 = (string)($_POST['new_customer_password_repeat'] ?? '');
        if ($customerId === '' || $currentPass === '' || $newPass === '' || $newPass2 === '') {
            flash('err', 'Tum alanlar zorunludur.');
            go(routeFromReq());
        }
        if ($newPass !== $newPass2) {
            flash('err', 'Yeni musteri sifreleri eslesmiyor.');
            go(routeFromReq());
        }
        try {
            $master = pdoConnect($MASTER_DB['host'], (int)$MASTER_DB['port'], $MASTER_DB['name'], $MASTER_DB['user'], $MASTER_DB['pass']);
            $st = $master->prepare("SELECT password_hash FROM customers WHERE id=:id LIMIT 1");
            $st->execute([':id' => $customerId]);
            $row = $st->fetch();
            if (!$row) {
                throw new RuntimeException('Musteri kaydi bulunamadi.');
            }
            $stored = trim((string)($row['password_hash'] ?? ''));
            $ok = false;
            if ($stored !== '') {
                $ok = password_verify($currentPass, $stored);
                if (!$ok && strlen($stored) === 32 && preg_match('/^[a-f0-9]{32}$/i', $stored)) {
                    $ok = hash_equals(strtolower($stored), md5($currentPass));
                }
                if (!$ok) {
                    $ok = hash_equals($stored, $currentPass);
                }
            }
            if (!$ok) {
                throw new RuntimeException('Mevcut musteri sifresi hatali.');
            }
            $newHash = password_hash($newPass, PASSWORD_DEFAULT);
            if (!is_string($newHash) || $newHash === '') {
                throw new RuntimeException('Sifre hash olusturulamadi.');
            }
            $master->prepare("UPDATE customers SET password_hash=:h WHERE id=:id")->execute([':h' => $newHash, ':id' => $customerId]);
            ensureCustomerRememberTokensTable($master);
            $master->prepare("DELETE FROM customer_remember_tokens WHERE customer_id=:cid")->execute([':cid' => $customerId]);
            rememberClearCookie($rememberCookieName);
            flash('ok', 'Musteri sifresi degistirildi. Beni Hatirla oturumlari kapatildi.');
        } catch (Throwable $ex) {
            flash('err', 'Musteri sifresi degistirilemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'settings_user_add') {
        if (!$isAdmin) {
            flash('err', 'Bu islem sadece yonetici icindir.');
            go(routeFromReq());
        }
        $username = trim((string)($_POST['new_username'] ?? ''));
        $fullName = trim((string)($_POST['new_full_name'] ?? ''));
        $role = trim((string)($_POST['new_role'] ?? 'staff'));
        $password = trim((string)($_POST['new_password'] ?? ''));
        $permRaw = trim((string)($_POST['new_permissions_raw'] ?? ''));
        $permSelected = $_POST['new_permissions'] ?? [];
        $scopeDepotSelected = $_POST['new_scope_depots'] ?? [];
        $scopeCashSelected = $_POST['new_scope_cash_accounts'] ?? [];
        $scopeCardSelected = $_POST['new_scope_card_accounts'] ?? [];
        if ($username === '' || $fullName === '') {
            flash('err', 'Kullanici adi ve ad soyad zorunludur.');
            go(routeFromReq());
        }
        if (!in_array($role, ['admin', 'staff'], true)) {
            $role = 'staff';
        }
        try {
            $st = $tenant->prepare("SELECT id FROM users WHERE profile_id=:pid AND username=:u AND is_deleted=0 LIMIT 1");
            $st->execute([':pid' => $profileId, ':u' => $username]);
            if ($st->fetch()) {
                throw new RuntimeException('Bu kullanici zaten var.');
            }
            $uid = uuid4();
            $hash = $password === '' ? '' : pbkdfHash($password);
            $tenant->prepare("INSERT INTO users (id,profile_id,username,full_name,password_hash,role,is_active,is_deleted) VALUES (:id,:pid,:u,:f,:p,:r,1,0)")
                ->execute([':id' => $uid, ':pid' => $profileId, ':u' => $username, ':f' => $fullName, ':p' => $hash, ':r' => $role]);
            $userAfterRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $uid);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'users',
                'ADD',
                $uid,
                null,
                $userAfterRow,
                ['source' => 'web.settings_user_add']
            );
            $validPerms = array_keys($AVAILABLE_PERMISSION_OPTIONS);
            $validPermMap = array_fill_keys($validPerms, true);
            $perms = [];
            if (is_array($permSelected)) {
                foreach ($permSelected as $pc) {
                    $pv = trim((string)$pc);
                    if ($pv !== '' && isset($validPermMap[$pv])) {
                        $perms[] = $pv;
                    }
                }
            }
            if (!$perms && $permRaw !== '') {
                foreach (preg_split('/[\s,;]+/', $permRaw) ?: [] as $pc) {
                    $pv = trim((string)$pc);
                    if ($pv !== '' && isset($validPermMap[$pv])) {
                        $perms[] = $pv;
                    }
                }
            }
            $permHasId = syncTableHasColumn($tenant, 'user_permissions', 'id');
            foreach (array_values(array_unique($perms)) as $pc) {
                $permId = $permHasId ? uuid4() : settingRecordId($uid, 'perm:' . $pc);
                if ($permHasId) {
                    $tenant->prepare("INSERT INTO user_permissions (id,user_id,permission_code) VALUES (:id,:uid,:pc)")
                        ->execute([':id' => $permId, ':uid' => $uid, ':pc' => $pc]);
                    $permAfterRow = syncFetchRowSnapshot($tenant, 'user_permissions', $profileId, $permId);
                } else {
                    $tenant->prepare("INSERT INTO user_permissions (user_id,permission_code) VALUES (:uid,:pc)")
                        ->execute([':uid' => $uid, ':pc' => $pc]);
                    $permAfterRow = ['user_id' => $uid, 'permission_code' => $pc];
                }
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'user_permissions',
                    'ADD',
                    $permId,
                    null,
                    $permAfterRow,
                    ['source' => 'web.settings_user_add']
                );
            }
            if ($role !== 'admin') {
                $normalizeScopeIds = static function ($value): array {
                    $out = [];
                    if (!is_array($value)) {
                        return $out;
                    }
                    foreach ($value as $item) {
                        $id = trim((string)$item);
                        if ($id !== '') {
                            $out[$id] = true;
                        }
                    }
                    return array_keys($out);
                };
                $scopeDepots = $normalizeScopeIds($scopeDepotSelected);
                $scopeCash = $normalizeScopeIds($scopeCashSelected);
                $scopeCard = $normalizeScopeIds($scopeCardSelected);
                $validDepotMap = [];
                $validCashMap = [];
                $validCardMap = [];
                $scopeHasId = syncTableHasColumn($tenant, 'user_access_scopes', 'id');

                $stDep = $tenant->prepare("SELECT id FROM depots WHERE profile_id=:pid AND is_deleted=0");
                $stDep->execute([':pid' => $profileId]);
                foreach ($stDep->fetchAll() ?: [] as $row) {
                    $rid = trim((string)($row['id'] ?? ''));
                    if ($rid !== '') {
                        $validDepotMap[$rid] = true;
                    }
                }
                $stAcc = $tenant->prepare("SELECT id,type FROM financial_accounts WHERE profile_id=:pid AND is_deleted=0 AND type IN ('cash','card')");
                $stAcc->execute([':pid' => $profileId]);
                foreach ($stAcc->fetchAll() ?: [] as $row) {
                    $rid = trim((string)($row['id'] ?? ''));
                    $rtype = trim((string)($row['type'] ?? ''));
                    if ($rid === '') {
                        continue;
                    }
                    if ($rtype === 'cash') {
                        $validCashMap[$rid] = true;
                    } elseif ($rtype === 'card') {
                        $validCardMap[$rid] = true;
                    }
                }
                foreach ($scopeDepots as $targetId) {
                    if (!isset($validDepotMap[$targetId])) {
                        continue;
                    }
                    if ($scopeHasId) {
                        $scopeId = uuid4();
                        $tenant->prepare("INSERT INTO user_access_scopes (id,user_id,scope_type,target_id) VALUES (:id,:uid,'depot',:tid)")
                            ->execute([':id' => $scopeId, ':uid' => $uid, ':tid' => $targetId]);
                        $scopeAfterRow = syncFetchRowSnapshot($tenant, 'user_access_scopes', $profileId, $scopeId);
                        syncEnqueueGlobalEvent(
                            $tenant,
                            $profileId,
                            'user_access_scopes',
                            'ADD',
                            $scopeId,
                            null,
                            $scopeAfterRow,
                            ['source' => 'web.settings_user_add']
                        );
                    } else {
                        $tenant->prepare("INSERT INTO user_access_scopes (user_id,scope_type,target_id) VALUES (:uid,'depot',:tid)")
                            ->execute([':uid' => $uid, ':tid' => $targetId]);
                        $scopeAfterRow = ['user_id' => $uid, 'scope_type' => 'depot', 'target_id' => $targetId];
                        $scopeId = settingRecordId($uid, 'scope:depot:' . $targetId);
                        syncEnqueueGlobalEvent(
                            $tenant,
                            $profileId,
                            'user_access_scopes',
                            'ADD',
                            $scopeId,
                            null,
                            $scopeAfterRow,
                            ['source' => 'web.settings_user_add']
                        );
                    }
                }
                foreach ($scopeCash as $targetId) {
                    if (!isset($validCashMap[$targetId])) {
                        continue;
                    }
                    if ($scopeHasId) {
                        $scopeId = uuid4();
                        $tenant->prepare("INSERT INTO user_access_scopes (id,user_id,scope_type,target_id) VALUES (:id,:uid,'cash_account',:tid)")
                            ->execute([':id' => $scopeId, ':uid' => $uid, ':tid' => $targetId]);
                        $scopeAfterRow = syncFetchRowSnapshot($tenant, 'user_access_scopes', $profileId, $scopeId);
                        syncEnqueueGlobalEvent(
                            $tenant,
                            $profileId,
                            'user_access_scopes',
                            'ADD',
                            $scopeId,
                            null,
                            $scopeAfterRow,
                            ['source' => 'web.settings_user_add']
                        );
                    } else {
                        $tenant->prepare("INSERT INTO user_access_scopes (user_id,scope_type,target_id) VALUES (:uid,'cash_account',:tid)")
                            ->execute([':uid' => $uid, ':tid' => $targetId]);
                        $scopeAfterRow = ['user_id' => $uid, 'scope_type' => 'cash_account', 'target_id' => $targetId];
                        $scopeId = settingRecordId($uid, 'scope:cash_account:' . $targetId);
                        syncEnqueueGlobalEvent(
                            $tenant,
                            $profileId,
                            'user_access_scopes',
                            'ADD',
                            $scopeId,
                            null,
                            $scopeAfterRow,
                            ['source' => 'web.settings_user_add']
                        );
                    }
                }
                foreach ($scopeCard as $targetId) {
                    if (!isset($validCardMap[$targetId])) {
                        continue;
                    }
                    if ($scopeHasId) {
                        $scopeId = uuid4();
                        $tenant->prepare("INSERT INTO user_access_scopes (id,user_id,scope_type,target_id) VALUES (:id,:uid,'card_account',:tid)")
                            ->execute([':id' => $scopeId, ':uid' => $uid, ':tid' => $targetId]);
                        $scopeAfterRow = syncFetchRowSnapshot($tenant, 'user_access_scopes', $profileId, $scopeId);
                        syncEnqueueGlobalEvent(
                            $tenant,
                            $profileId,
                            'user_access_scopes',
                            'ADD',
                            $scopeId,
                            null,
                            $scopeAfterRow,
                            ['source' => 'web.settings_user_add']
                        );
                    } else {
                        $tenant->prepare("INSERT INTO user_access_scopes (user_id,scope_type,target_id) VALUES (:uid,'card_account',:tid)")
                            ->execute([':uid' => $uid, ':tid' => $targetId]);
                        $scopeAfterRow = ['user_id' => $uid, 'scope_type' => 'card_account', 'target_id' => $targetId];
                        $scopeId = settingRecordId($uid, 'scope:card_account:' . $targetId);
                        syncEnqueueGlobalEvent(
                            $tenant,
                            $profileId,
                            'user_access_scopes',
                            'ADD',
                            $scopeId,
                            null,
                            $scopeAfterRow,
                            ['source' => 'web.settings_user_add']
                        );
                    }
                }
            }
            flash('ok', 'Kullanici eklendi.');
        } catch (Throwable $ex) {
            flash('err', 'Kullanici eklenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'settings_user_toggle') {
        if (!$isAdmin) {
            flash('err', 'Bu islem sadece yonetici icindir.');
            go(routeFromReq());
        }
        $uid = trim((string)($_POST['user_id'] ?? ''));
        $active = !empty($_POST['is_active']) ? 1 : 0;
        if ($uid === '') {
            flash('err', 'Kullanici secilmedi.');
            go(routeFromReq());
        }
        try {
            $beforeRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $uid);
            $tenant->prepare("UPDATE users SET is_active=:a WHERE id=:id AND profile_id=:pid AND is_deleted=0")
                ->execute([':a' => $active, ':id' => $uid, ':pid' => $profileId]);
            $afterRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $uid);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'users',
                'UPDATE',
                $uid,
                $beforeRow,
                $afterRow,
                ['source' => 'web.settings_user_toggle']
            );
            flash('ok', 'Kullanici durumu guncellendi.');
        } catch (Throwable $ex) {
            flash('err', 'Kullanici guncellenemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'settings_user_delete') {
        if (!$isAdmin) {
            flash('err', 'Bu islem sadece yonetici icindir.');
            go(routeFromReq());
        }
        $uid = trim((string)($_POST['user_id'] ?? ''));
        if ($uid === '') {
            flash('err', 'Kullanici secilmedi.');
            go(routeFromReq());
        }
        try {
            $beforeUserRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $uid);
            $permHasId = syncTableHasColumn($tenant, 'user_permissions', 'id');
            $scopeHasId = syncTableHasColumn($tenant, 'user_access_scopes', 'id');
            $permissionBeforeRows = [];
            $scopeBeforeRows = [];
            if ($permHasId) {
                $permSt = $tenant->prepare("SELECT id FROM user_permissions WHERE user_id=:uid");
                $permSt->execute([':uid' => $uid]);
                foreach ($permSt->fetchAll() ?: [] as $row) {
                    $permId = trim((string)($row['id'] ?? ''));
                    if ($permId === '') {
                        continue;
                    }
                    $permissionBeforeRows[$permId] = syncFetchRowSnapshot($tenant, 'user_permissions', $profileId, $permId);
                }
            } else {
                $permSt = $tenant->prepare("SELECT user_id,permission_code FROM user_permissions WHERE user_id=:uid");
                $permSt->execute([':uid' => $uid]);
                foreach ($permSt->fetchAll() ?: [] as $row) {
                    $permCode = trim((string)($row['permission_code'] ?? ''));
                    if ($permCode === '') {
                        continue;
                    }
                    $permId = settingRecordId($uid, 'perm:' . $permCode);
                    $permissionBeforeRows[$permId] = [
                        'user_id' => $uid,
                        'permission_code' => $permCode,
                    ];
                }
            }
            if ($scopeHasId) {
                $scopeSt = $tenant->prepare("SELECT id FROM user_access_scopes WHERE user_id=:uid");
                $scopeSt->execute([':uid' => $uid]);
                foreach ($scopeSt->fetchAll() ?: [] as $row) {
                    $scopeId = trim((string)($row['id'] ?? ''));
                    if ($scopeId === '') {
                        continue;
                    }
                    $scopeBeforeRows[$scopeId] = syncFetchRowSnapshot($tenant, 'user_access_scopes', $profileId, $scopeId);
                }
            } else {
                $scopeSt = $tenant->prepare("SELECT user_id,scope_type,target_id FROM user_access_scopes WHERE user_id=:uid");
                $scopeSt->execute([':uid' => $uid]);
                foreach ($scopeSt->fetchAll() ?: [] as $row) {
                    $scopeType = trim((string)($row['scope_type'] ?? ''));
                    $targetId = trim((string)($row['target_id'] ?? ''));
                    if ($scopeType === '' || $targetId === '') {
                        continue;
                    }
                    $scopeId = settingRecordId($uid, 'scope:' . $scopeType . ':' . $targetId);
                    $scopeBeforeRows[$scopeId] = [
                        'user_id' => $uid,
                        'scope_type' => $scopeType,
                        'target_id' => $targetId,
                    ];
                }
            }
            $tenant->prepare("UPDATE users SET is_deleted=1,is_active=0 WHERE id=:id AND profile_id=:pid")
                ->execute([':id' => $uid, ':pid' => $profileId]);
            $afterUserRow = syncFetchRowSnapshot($tenant, 'users', $profileId, $uid);
            syncEnqueueGlobalEvent(
                $tenant,
                $profileId,
                'users',
                'UPDATE',
                $uid,
                $beforeUserRow,
                $afterUserRow,
                ['source' => 'web.settings_user_delete']
            );
            $tenant->prepare("DELETE FROM user_permissions WHERE user_id=:uid")->execute([':uid' => $uid]);
            foreach ($permissionBeforeRows as $permId => $beforeRow) {
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'user_permissions',
                    'DELETE',
                    (string)$permId,
                    $beforeRow,
                    null,
                    ['source' => 'web.settings_user_delete']
                );
            }
            $tenant->prepare("DELETE FROM user_access_scopes WHERE user_id=:uid")->execute([':uid' => $uid]);
            foreach ($scopeBeforeRows as $scopeId => $beforeRow) {
                syncEnqueueGlobalEvent(
                    $tenant,
                    $profileId,
                    'user_access_scopes',
                    'DELETE',
                    (string)$scopeId,
                    $beforeRow,
                    null,
                    ['source' => 'web.settings_user_delete']
                );
            }
            flash('ok', 'Kullanici silindi.');
        } catch (Throwable $ex) {
            flash('err', 'Kullanici silinemedi: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }

    if ($act === 'settings_transfer_data') {
        if (!$isAdmin) {
            flash('err', 'Bu islem sadece yonetici icindir.');
            go(routeFromReq());
        }
        $targetProfileId = trim((string)($_POST['target_profile_id'] ?? ''));
        $sections = $_POST['transfer_sections'] ?? [];
        if ($targetProfileId === '' || $targetProfileId === $profileId) {
            flash('err', 'Gecerli hedef profil seciniz.');
            go(routeFromReq());
        }
        if (!is_array($sections) || !$sections) {
            flash('err', 'Aktarim icin en az bir bolum seciniz.');
            go(routeFromReq());
        }
        try {
            $tenant->beginTransaction();
            if (in_array('persons', $sections, true)) {
                $tenant->prepare("INSERT INTO persons (id,profile_id,full_name,phone_number,address,notes,is_deleted,created_at,updated_at) SELECT UUID(), :toId, s.full_name, s.phone_number, s.address, s.notes, 0, NOW(), NOW() FROM persons s LEFT JOIN persons t ON t.profile_id=:toId AND t.full_name=s.full_name AND t.is_deleted=0 WHERE s.profile_id=:fromId AND s.is_deleted=0 AND t.id IS NULL")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            if (in_array('depots', $sections, true)) {
                $tenant->prepare("INSERT INTO depots (id,profile_id,name,is_deleted,created_at,updated_at) SELECT UUID(), :toId, s.name, 0, NOW(), NOW() FROM depots s LEFT JOIN depots t ON t.profile_id=:toId AND t.name=s.name AND t.is_deleted=0 WHERE s.profile_id=:fromId AND s.is_deleted=0 AND t.id IS NULL")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            if (in_array('products', $sections, true)) {
                $tenant->prepare("INSERT INTO product_definitions (id,profile_id,stock_type,name,barcode_header,karat_purity,workmanship_fee,workmanship_currency,default_purchase_price,default_sale_price,purchase_currency,sale_currency,default_weight_gr,is_active,is_deleted,created_at,updated_at) SELECT UUID(), :toId, s.stock_type, s.name, s.barcode_header, s.karat_purity, s.workmanship_fee, s.workmanship_currency, s.default_purchase_price, s.default_sale_price, s.purchase_currency, s.sale_currency, s.default_weight_gr, s.is_active, 0, NOW(), NOW() FROM product_definitions s LEFT JOIN product_definitions t ON t.profile_id=:toId AND t.stock_type=s.stock_type AND t.name=s.name AND t.is_deleted=0 WHERE s.profile_id=:fromId AND s.is_deleted=0 AND t.id IS NULL")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            if (in_array('accounts', $sections, true)) {
                $tenant->prepare("INSERT INTO financial_accounts (id,profile_id,name,type,is_deleted,created_at,updated_at) SELECT UUID(), :toId, s.name, s.type, 0, NOW(), NOW() FROM financial_accounts s LEFT JOIN financial_accounts t ON t.profile_id=:toId AND t.name=s.name AND t.type=s.type AND t.is_deleted=0 WHERE s.profile_id=:fromId AND s.is_deleted=0 AND t.id IS NULL")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            if (in_array('services', $sections, true)) {
                $tenant->prepare("INSERT INTO service_definitions (id,profile_id,name,custom_columns_json,clone_columns_json) SELECT UUID(), :toId, s.name, s.custom_columns_json, s.clone_columns_json FROM service_definitions s LEFT JOIN service_definitions t ON t.profile_id=:toId AND t.name=s.name WHERE s.profile_id=:fromId AND t.id IS NULL")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
                $tenant->prepare("INSERT INTO service_jobs (id,profile_id,service_def_id,customer_name,description,status,fee,currency,start_date,delivery_date,dynamic_data_json) SELECT UUID(), :toId, td.id, j.customer_name, j.description, j.status, j.fee, j.currency, j.start_date, j.delivery_date, j.dynamic_data_json FROM service_jobs j INNER JOIN service_definitions sd ON sd.id=j.service_def_id AND sd.profile_id=:fromId INNER JOIN service_definitions td ON td.profile_id=:toId AND td.name=sd.name WHERE j.profile_id=:fromId")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            if (in_array('repairs', $sections, true)) {
                $tenant->prepare("INSERT INTO repair_records (id,profile_id,customer_full_name,product_description,complaint_request,receipt_date,estimated_fee,fee_currency,status,delivery_date,notes,is_deleted,created_at,updated_at) SELECT UUID(), :toId, r.customer_full_name, r.product_description, r.complaint_request, r.receipt_date, r.estimated_fee, r.fee_currency, r.status, r.delivery_date, r.notes, 0, NOW(), NOW() FROM repair_records r WHERE r.profile_id=:fromId AND r.is_deleted=0")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            if (in_array('agenda', $sections, true)) {
                $tenant->prepare("INSERT INTO agenda_notes (profile_id,note_key,note_value) SELECT :toId, note_key, note_value FROM agenda_notes WHERE profile_id=:fromId ON DUPLICATE KEY UPDATE note_value=VALUES(note_value)")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            if (in_array('settings', $sections, true)) {
                $tenant->prepare("INSERT INTO app_settings (profile_id,setting_key,setting_value,setting_type) SELECT :toId, setting_key, setting_value, setting_type FROM app_settings WHERE profile_id=:fromId AND setting_key IN ('document_prefix','shop_name','shop_phone','company_name','company_address','tax_office','tax_number','company_phone','company_email','display_currency','theme_name','custom_themes_json') ON DUPLICATE KEY UPDATE setting_value=VALUES(setting_value), setting_type=VALUES(setting_type)")
                    ->execute([':toId' => $targetProfileId, ':fromId' => $profileId]);
            }
            $tenant->commit();
            flash('ok', 'Veri aktarimi tamamlandi.');
        } catch (Throwable $ex) {
            if ($tenant->inTransaction()) {
                $tenant->rollBack();
            }
            flash('err', 'Veri aktarimi hatasi: ' . $ex->getMessage());
        }
        go(routeFromReq());

    if (in_array($act, ['settings_device_rename', 'settings_device_remove', 'settings_device_logout', 'settings_web_session_close'], true)) {
        if (!$isAdmin && !hasPerm($user, 'perm_accounts')) {
            flash('err', 'Bu islem icin yetkiniz yok.');
            go(routeFromReq());
        }

        $customerId = (int)($_SESSION['customer']['id'] ?? 0);
        $deviceId = (int)($_POST['device_id'] ?? 0);
        if ($customerId <= 0) {
            flash('err', 'Gecerli musteri oturumu bulunamadi.');
            go(routeFromReq());
        }

        try {
            $master = golder_master_pdo();
            if ($act === 'settings_web_session_close') {
                $sessionUuid = trim((string)($_POST['session_uuid'] ?? ''));
                if ($sessionUuid === '') {
                    throw new RuntimeException('Gecerli web oturumu secilmedi.');
                }
                golder_close_web_session($master, $sessionUuid, 'manual_logout');
                flash('ok', 'Web oturumu kapatildi.');
            } elseif ($act === 'settings_device_rename') {
                if ($deviceId <= 0) {
                    throw new RuntimeException('Gecerli cihaz secimi yapiniz.');
                }
                $deviceName = trim((string)($_POST['device_name'] ?? ''));
                if ($deviceName === '') {
                    throw new RuntimeException('Cihaz adi bos olamaz.');
                }
                if (!golder_rename_device($master, $customerId, $deviceId, $deviceName)) {
                    throw new RuntimeException('Cihaz adi guncellenemedi.');
                }
                flash('ok', 'Cihaz adi guncellendi.');
            } elseif ($act === 'settings_device_remove') {
                if ($deviceId <= 0) {
                    throw new RuntimeException('Gecerli cihaz secimi yapiniz.');
                }
                if (!golder_remove_device($master, $customerId, $deviceId)) {
                    throw new RuntimeException('Cihaz listeden kaldirilamadi.');
                }
                flash('ok', 'Cihaz listeden kaldirildi.');
            } else {
                if ($deviceId <= 0) {
                    throw new RuntimeException('Gecerli cihaz secimi yapiniz.');
                }
                $closed = golder_close_web_sessions_by_device($master, $customerId, $deviceId, 'manual_logout');
                flash('ok', 'Cihaz web oturumlari kapatildi (' . (int)$closed . ').');
            }
        } catch (Throwable $ex) {
            flash('err', 'Cihaz islemi basarisiz: ' . $ex->getMessage());
        }
        go(routeFromReq());
    }


}}

$profiles = [];
$users = [];
$accounts = [];
$persons = [];
$productNames = [];
$ziynetNames = [];
$scrapNames = [];
$productMeta = [];
$ziynetMeta = [];
$scrapMeta = [];
$depotNames = [];
$debtsReceivable = [];
$debtsPayable = [];
$historyRows = [];
$historyRowsWithBalance = [];
$historyEditRow = null;
$personBalanceRows = [];
$moneyBalanceRows = [];
$stockRows = [];
$stockStatusRows = [];
$stockDefinitionRows = [];
$stockCategoryRows = [];
$depotStockRows = [];
$serviceDefinitionRows = [];
$serviceJobRows = [];
$repairRows = [];
$agendaRows = [];
$toolSearchRows = [];
$settingsUsers = [];
$profileOptionsAll = [];
$generalSettings = [];
$settingsDepotOptions = [];
$settingsCashAccountOptions = [];
$settingsCardAccountOptions = [];
$stockBarcodeRows = [];
$barcodeMeta = [];
$toolReportRows = [];
$toolReportHeaders = [];
$toolReportSummary = [];
$taxDefinitionRows = [];
$taxProfileRows = [];
$taxProfileItemsMap = [];
$taxLedgerRows = [];
$activeTaxRows = [];
$settingsDeviceRows = [];
$settingsWebSessionRows = [];
$dashboardAccountCards = [];
$dashboardRecentTransactions = [];
$dashboardRates = [];
$dashboardRateRows = [];
$dashboardRatesUpdatedAt = '';
$serviceJobFilters = [
    'status' => '',
    'definition_id' => '',
    'customer' => '',
];
$serviceDefinitionMap = [];
$zakatDisplayCurrency = 'TRY';
$zakatStoreDate = date('Y-m-d', strtotime('-355 days'));
$zakatStoreReport = null;
$zakatGuestResult = null;
$zakatGuestItems = [];
$zakatGuestDebt = '';
$zakatGuestDebtCurrency = 'TRY';
$zakatGuestTargetCurrency = 'TRY';
$isAdmin = (!empty($_SESSION['user']) && (($_SESSION['user']['role'] ?? '') === 'admin'));
$selectedPerson = trim((string)($_GET['person'] ?? ''));
$selectedOp = strtolower(trim((string)($_GET['op'] ?? '')));
if ($selectedOp === 'iman') {
    $selectedOp = 'virman';
}
$allowedOps = ['cash', 'gold', 'ziynet', 'scrap', 'exchange', 'virman'];
if (!in_array($selectedOp, $allowedOps, true)) {
    $selectedOp = '';
}
$themeVars = resolveThemeVarsFromName('Green Harmony');
$cat = trim((string)($_GET['cat'] ?? ''));
$sub = trim((string)($_GET['sub'] ?? ''));
[$cat, $sub] = normalizeCatSub($cat, $sub);
$cats = [
    'shopping' => [
        'title' => ui_text('trading_tab', 'Alışveriş', 'Shopping'),
        'subs' => [
            ['id' => 'shopping-product', 'title' => ui_text('web_shopping_product', 'Altın Alış / Satış', 'Gold Purchase / Sale'), 'any' => ['perm_product_purchase', 'perm_product_sale', 'perm_shopping']],
            ['id' => 'shopping-scrap', 'title' => ui_text('web_shopping_scrap', 'Hurda Alış / Satış', 'Scrap Purchase / Sale'), 'any' => ['perm_scrap_purchase', 'perm_scrap_sale', 'perm_shopping']],
            ['id' => 'shopping-ziynet', 'title' => ui_text('web_shopping_ziynet', 'Ziynet Alış / Satış', 'Ziynet Purchase / Sale'), 'any' => ['perm_ziynet_purchase', 'perm_ziynet_sale', 'perm_shopping']],
        ],
    ],
    'finance' => [
        'title' => ui_text('cat_finance', 'Para İşlemleri', 'Finance'),
        'subs' => [
            ['id' => 'finance-cashflow', 'title' => ui_text('web_finance_cashflow', 'Gelir / Gider Ekle', 'Add Income / Expense'), 'any' => ['perm_add_income', 'perm_add_expense']],
            ['id' => 'finance-transfer', 'title' => ui_text('account_transfer_tab', 'Hesaplar Arası Transfer', 'Account Transfer'), 'perm' => 'perm_transfer'],
            ['id' => 'finance-accounts', 'title' => ui_text('web_finance_accounts', 'Kasa & Kart Durumu', 'Cash & Card Status'), 'any' => ['perm_cash_view', 'perm_card_view']],
        ],
    ],
    'stocks' => [
        'title' => ui_text('stocks_tab', 'Stoklar & Tanımlar', 'Stocks & Definitions'),
        'subs' => [
            ['id' => 'stock-product', 'title' => ui_text('product_status_tab', 'Altın Stoğu', 'Gold Stock'), 'perm' => 'perm_stock_product'],
            ['id' => 'stock-scrap', 'title' => ui_text('scrap_status_tab', 'Hurda Stoğu', 'Scrap Stock'), 'perm' => 'perm_stock_scrap'],
            ['id' => 'stock-ziynet', 'title' => ui_text('ziynet_status_tab', 'Ziynet Stoğu', 'Ziynet Stock'), 'perm' => 'perm_stock_ziynet'],
            ['id' => 'stock-def-product', 'title' => ui_text('product_def_tab', 'Altın Tanımlama', 'Gold Definitions'), 'perm' => 'perm_def_product'],
            ['id' => 'stock-def-scrap', 'title' => ui_text('scrap_def_tab', 'Hurda Tanımlama', 'Scrap Definitions'), 'perm' => 'perm_def_scrap'],
            ['id' => 'stock-def-ziynet', 'title' => ui_text('ziynet_def_tab', 'Ziynet Tanımlama', 'Ziynet Definitions'), 'perm' => 'perm_def_ziynet'],
            ['id' => 'stock-depots', 'title' => ui_text('web_stock_depots', 'Depolar', 'Depots'), 'perm' => 'perm_depots'],
            ['id' => 'stock-categories', 'title' => ui_text('categories', 'Kategoriler', 'Categories'), 'any' => ['perm_def_product', 'perm_def_scrap', 'perm_def_ziynet']],
        ],
    ],
    'customers' => [
        'title' => ui_text('cat_crm', 'Müşteri İşlemleri', 'Customers & Accounts'),
        'subs' => [
            ['id' => 'customer-persons', 'title' => ui_text('contacts_tab', 'Müşteri Yönetimi', 'Customer Management'), 'perm' => 'perm_contacts'],
            ['id' => 'customer-accounts', 'title' => ui_text('accounts_tab', 'Cari Hesaplar', 'Accounts'), 'perm' => 'perm_accounts'],
        ],
    ],
    'services' => [
        'title' => ui_text('cat_services', 'Hizmetler ve Tamir', 'Services & Repair'),
        'subs' => [
            ['id' => 'services-jobs', 'title' => ui_text('web_services_jobs', 'Hizmetler & İşlemler', 'Services & Jobs'), 'perm' => 'perm_services'],
            ['id' => 'services-repair', 'title' => ui_text('repair_tab', 'Tamir Takip', 'Repair Tracking'), 'perm' => 'perm_repair'],
        ],
    ],
    'tools' => [
        'title' => ui_text('cat_tools', 'Araçlar', 'Tools'),
        'subs' => [
            ['id' => 'tools-reports', 'title' => ui_text('report_tab', 'Raporlama', 'Reporting'), 'perm' => 'perm_reports'],
            ['id' => 'tools-agenda', 'title' => ui_text('agenda_tab', 'Ajanda / Takvim', 'Agenda / Calendar'), 'perm' => 'perm_agenda'],
            ['id' => 'tools-search', 'title' => ui_text('perm_search', 'Arama / Barkod Sorgu', 'Search / Barcode Query'), 'perm' => 'perm_search'],
            ['id' => 'tools-calculator', 'title' => ui_text('calculator_tab', 'Hesap Makinesi', 'Calculator'), 'perm' => 'perm_calculator'],
            ['id' => 'tools-zakat', 'title' => ui_text('web_zakat_title', 'Zekat Hesaplama', 'Zakat Calculator'), 'any' => ['perm_zakat', 'perm_zakat_guest']],
        ],
    ],
    'settings' => [
        'title' => ui_text('settings_tab', 'Ayarlar', 'Settings'),
        'subs' => [
            ['id' => 'settings-general', 'title' => ui_text('general_settings', 'Genel Ayarlar', 'General Settings'), 'perm' => 'perm_accounts'],
            ['id' => 'settings-security', 'title' => ui_text('security', 'Güvenlik Ayarları', 'Security Settings'), 'perm' => 'perm_accounts'],
            ['id' => 'settings-users', 'title' => ui_text('web_settings_users', 'Kullanıcı / Personel', 'Users / Staff'), 'perm' => 'perm_accounts'],
            ['id' => 'settings-taxes', 'title' => ui_text('taxes', 'Vergiler', 'Taxes'), 'perm' => 'perm_accounts'],
            ['id' => 'settings-transfer', 'title' => ui_text('data_transfer', 'Veri Aktarımı', 'Data Transfer'), 'perm' => 'perm_accounts'],
            ['id' => 'settings-devices', 'title' => ui_text('web_settings_devices', 'Cihaz Yönetimi', 'Device Management'), 'perm' => 'perm_accounts'],
        ],
    ],
];
$isShoppingSub = in_array($sub, ['shopping-product', 'shopping-scrap', 'shopping-ziynet'], true);
$isCustomerAccountsSub = ($sub === 'customer-accounts') || $isShoppingSub;
if ($isShoppingSub && $selectedOp === '') {
    if ($sub === 'shopping-scrap') {
        $selectedOp = 'scrap';
    } elseif ($sub === 'shopping-ziynet') {
        $selectedOp = 'ziynet';
    } else {
        $selectedOp = 'gold';
    }
}
if ($isCustomerAccountsSub && $selectedPerson !== '' && $selectedOp === '') {
    $selectedOp = 'cash';
}

if ($tenant instanceof PDO && !empty($_SESSION['customer']) && empty($_SESSION['profile'])) {
    try {
        $profiles = $tenant->query("SELECT id,name,is_active FROM profiles WHERE is_deleted=0 ORDER BY name ASC")->fetchAll() ?: [];
    } catch (Throwable $ex) {
        $tenantError = 'Profil listesi okunamadı: ' . $ex->getMessage();
    }
}

if ($tenant instanceof PDO && !empty($_SESSION['profile']) && empty($_SESSION['user'])) {
    try {
        $profileId = (string)$_SESSION['profile']['id'];
        ensureDefaultAdmin($tenant, $profileId);
        $bootstrapAdmin = getPasswordlessBootstrapAdmin($tenant, $profileId);
        if ($bootstrapAdmin) {
            $_SESSION['user'] = [
                'id' => (string)$bootstrapAdmin['id'],
                'username' => (string)$bootstrapAdmin['username'],
                'full_name' => (string)($bootstrapAdmin['full_name'] ?? $bootstrapAdmin['username']),
                'role' => 'admin',
                'permissions' => ['ALL'],
                'allowed_depots' => null,
                'allowed_cash_accounts' => null,
                'allowed_card_accounts' => null,
            ];
            flash('ok', 'Sifresiz profil icin yonetici oturumu otomatik acildi.');
            go();
        }
        $st = $tenant->prepare("SELECT username,full_name,role FROM users WHERE profile_id=:pid AND is_active=1 AND is_deleted=0 ORDER BY full_name ASC");
        $st->execute([':pid' => $profileId]);
        $users = $st->fetchAll() ?: [];
        $seenUserKeys = [];
        $uniqueUsers = [];
        foreach ($users as $userRow) {
            $userKey = strtolower(trim((string)($userRow['username'] ?? ''))) . '|' . strtolower(trim((string)($userRow['role'] ?? '')));
            if ($userKey === '' || isset($seenUserKeys[$userKey])) {
                continue;
            }
            $seenUserKeys[$userKey] = true;
            $uniqueUsers[] = $userRow;
        }
        $users = $uniqueUsers;
    } catch (Throwable $ex) {
        $tenantError = 'Kullanıcı listesi okunamadı: ' . $ex->getMessage();
    }
}

if ($tenant instanceof PDO && !empty($_SESSION['profile']) && !empty($_SESSION['user'])) {
    $profileId = (string)$_SESSION['profile']['id'];
    $user = $_SESSION['user'];
    $isAdmin = (($user['role'] ?? '') === 'admin');
    ensureStage5Tables($tenant);
    $themeVars = profileThemeVars($tenant, $profileId);
    try {
        $accounts = fetchAccounts($tenant, $profileId, $user);
        $allowedAccountMap = [];
        foreach ($accounts as $a) {
            $aid = (string)($a['id'] ?? '');
            if ($aid === '') {
                continue;
            }
            $allowedAccountMap[$aid] = [
                'name' => (string)($a['name'] ?? ''),
                'type' => (string)($a['type'] ?? ''),
            ];
        }

        if ($allowedAccountMap) {
            $ids = array_keys($allowedAccountMap);
            $inPlaceholders = implode(',', array_fill(0, count($ids), '?'));

            $sqlBalances = "SELECT fa.id account_id, fa.name account_name, fa.type account_type, ft.currency currency, COALESCE(SUM(CASE WHEN ft.transaction_type='Income' THEN ft.amount ELSE -ft.amount END),0) net_balance FROM financial_accounts fa LEFT JOIN financial_transactions ft ON ft.account_id=fa.id AND ft.profile_id=fa.profile_id AND ft.is_deleted=0 WHERE fa.profile_id=? AND fa.is_deleted=0 AND fa.id IN ($inPlaceholders) GROUP BY fa.id,fa.name,fa.type,ft.currency ORDER BY fa.type,fa.name,ft.currency";
            $stDash = $tenant->prepare($sqlBalances);
            $stDash->execute(array_merge([$profileId], $ids));
            $dashRows = $stDash->fetchAll() ?: [];

            $cardBalances = [];
            foreach ($dashRows as $row) {
                $accId = (string)($row['account_id'] ?? '');
                $curr = trim((string)($row['currency'] ?? ''));
                if ($accId === '' || $curr === '') {
                    continue;
                }
                $cardBalances[$accId][$curr] = (float)($row['net_balance'] ?? 0);
            }

            foreach ($allowedAccountMap as $accId => $meta) {
                $dashboardAccountCards[] = [
                    'account_id' => $accId,
                    'account_name' => $meta['name'],
                    'account_type' => $meta['type'],
                    'balances' => $cardBalances[$accId] ?? [],
                ];
            }

            $sqlRecent = "SELECT ft.transaction_date,ft.created_at,ft.transaction_type,ft.amount,ft.currency,ft.description,fa.name account_name,fa.type account_type FROM financial_transactions ft INNER JOIN financial_accounts fa ON fa.id=ft.account_id AND fa.profile_id=ft.profile_id WHERE ft.profile_id=? AND ft.is_deleted=0 AND ft.account_id IN ($inPlaceholders) ORDER BY ft.created_at DESC, ft.transaction_date DESC LIMIT 15";
            $stRecent = $tenant->prepare($sqlRecent);
            $stRecent->execute(array_merge([$profileId], $ids));
            $dashboardRecentTransactions = $stRecent->fetchAll() ?: [];
        }

        $dashboardRates = fetchDashboardRates($tenant, $profileId);
        $dashboardRateRows = dashboardRateRows($dashboardRates);
        $dashboardRatesUpdatedAt = trim((string)($dashboardRates['Update_Date'] ?? ''));

        $ps = $tenant->prepare("SELECT id,full_name,phone_number,address,notes FROM persons WHERE profile_id=:pid AND is_deleted=0 ORDER BY full_name ASC");
        $ps->execute([':pid' => $profileId]);
        $persons = $ps->fetchAll() ?: [];

        foreach ($SUPPORTED_STOCK_TYPES as $stockTypeName) {
            $prd = $tenant->prepare("SELECT name,default_purchase_price,default_sale_price,purchase_currency,sale_currency FROM product_definitions WHERE profile_id=:pid AND stock_type=:st AND is_deleted=0 ORDER BY name ASC");
            $prd->execute([':pid' => $profileId, ':st' => $stockTypeName]);
            foreach ($prd->fetchAll() as $r) {
                $n = trim((string)($r['name'] ?? ''));
                if ($n === '') {
                    continue;
                }
                $metaRow = [
                    'buy_price' => (float)($r['default_purchase_price'] ?? 0),
                    'sell_price' => (float)($r['default_sale_price'] ?? 0),
                    'buy_currency' => trim((string)($r['purchase_currency'] ?? 'TRY')),
                    'sell_currency' => trim((string)($r['sale_currency'] ?? 'TRY')),
                ];
                if ($stockTypeName === 'Product') {
                    $productNames[] = $n;
                    $productMeta[$n] = $metaRow;
                } elseif ($stockTypeName === 'Ziynet') {
                    $ziynetNames[] = $n;
                    $ziynetMeta[$n] = $metaRow;
                } else {
                    $scrapNames[] = $n;
                    $scrapMeta[$n] = $metaRow;
                }
            }
        }

        foreach (fetchDepots($tenant, $profileId, $user) as $r) {
            $n = trim((string)($r['name'] ?? ''));
            if ($n !== '') {
                $depotNames[] = $n;
            }
        }

        if ($isCustomerAccountsSub || $sub === 'customer-persons') {
            $stBarMeta = $tenant->prepare(
                "SELECT s.unique_barcode, s.quantity_gr, p.karat_purity
                 FROM stock_items s
                 LEFT JOIN product_definitions p
                   ON p.name=s.item_type_name AND p.profile_id=s.profile_id AND p.stock_type='Product'
                 WHERE s.profile_id=:pid
                   AND s.stock_type='Product'
                   AND s.is_deleted=0
                   AND s.quantity>0
                   AND s.unique_barcode<>''"
            );
            $stBarMeta->execute([':pid' => $profileId]);
            foreach ($stBarMeta->fetchAll() ?: [] as $row) {
                $bc = trim((string)($row['unique_barcode'] ?? ''));
                if ($bc === '') {
                    continue;
                }
                $barcodeMeta[$bc] = [
                    'gram' => (float)($row['quantity_gr'] ?? 0),
                    'karat' => trim((string)($row['karat_purity'] ?? '')),
                ];
            }
        }

        $pivotedAccounts = [];
        $allCurrencies = [];
        if ($sub === 'finance-accounts') {
            $st = $tenant->prepare("SELECT fa.id account_id, fa.name account_name, fa.type account_type, ft.currency currency, COALESCE(SUM(CASE WHEN ft.transaction_type='Income' THEN ft.amount ELSE -ft.amount END),0) net_balance FROM financial_accounts fa LEFT JOIN financial_transactions ft ON ft.account_id=fa.id AND ft.profile_id=fa.profile_id AND ft.is_deleted=0 WHERE fa.profile_id=:pid AND fa.is_deleted=0 GROUP BY fa.id,fa.name,fa.type,ft.currency ORDER BY fa.type,fa.name,ft.currency");
            $st->execute([':pid' => $profileId]);
            $all = $st->fetchAll() ?: [];

            foreach ($accounts as $a) {
                $pivotedAccounts[(string)$a['id']] = [
                    'name' => $a['name'],
                    'type' => $a['type'],
                    'balances' => []
                ];
            }
            foreach ($all as $r) {
                $aid = (string)$r['account_id'];
                $cur = trim((string)$r['currency']);
                $bal = (float)$r['net_balance'];

                if (isset($pivotedAccounts[$aid]) && $cur !== '') {
                    $pivotedAccounts[$aid]['balances'][$cur] = $bal;
                    if (!in_array($cur, $allCurrencies, true)) {
                        $allCurrencies[] = $cur;
                    }
                }
            }
            if (empty($allCurrencies)) {
                $allCurrencies[] = 'TRY';
            }
            sort($allCurrencies);
        }

        if (in_array($sub, ['stock-product', 'stock-scrap', 'stock-ziynet'], true)) {
            $stockTypeFilter = 'Product';
            if ($sub === 'stock-scrap') {
                $stockTypeFilter = 'Scrap';
            } elseif ($sub === 'stock-ziynet') {
                $stockTypeFilter = 'Ziynet';
            }
            $st = $tenant->prepare(
                "SELECT d.id depot_id, COALESCE(d.name,'Depo') depot_name, s.stock_type, s.item_type_name,
                        SUM(s.quantity) total_qty,
                        AVG(s.purchase_price_base) avg_purchase_price,
                        SUM(s.quantity * s.purchase_price_base) total_value_raw,
                        SUM(CASE WHEN s.unique_barcode IS NOT NULL AND s.unique_barcode <> '' THEN 1 ELSE 0 END) barcode_count
                 FROM stock_items s
                 LEFT JOIN depots d ON d.id=s.depot_id AND d.profile_id=s.profile_id
                 WHERE s.profile_id=:pid AND s.is_deleted=0 AND s.stock_type=:st
                 GROUP BY d.id,d.name,s.stock_type,s.item_type_name
                 ORDER BY d.name,s.item_type_name"
            );
            $st->execute([':pid' => $profileId, ':st' => $stockTypeFilter]);
            $rows = $st->fetchAll() ?: [];
            if (($user['role'] ?? '') !== 'admin') {
                $rows = array_values(array_filter($rows, static function ($row) use ($user): bool {
                    $depotId = trim((string)($row['depot_id'] ?? ''));
                    return $depotId !== '' && canAccessDepot($user, $depotId);
                }));
            }
            $stockStatusRows = $rows;
        }

        if (in_array($sub, ['stock-def-product', 'stock-def-scrap', 'stock-def-ziynet'], true)) {
            $stockTypeFilter = null;
            if ($sub === 'stock-def-product') {
                $stockTypeFilter = 'Product';
            } elseif ($sub === 'stock-def-scrap') {
                $stockTypeFilter = 'Scrap';
            } elseif ($sub === 'stock-def-ziynet') {
                $stockTypeFilter = 'Ziynet';
            }
            $st = $tenant->prepare("SELECT id,stock_type,name,karat_purity,default_purchase_price,purchase_currency,default_weight_gr FROM product_definitions WHERE profile_id=:pid AND is_deleted=0 ORDER BY stock_type,name");
            $st->execute([':pid' => $profileId]);
            $rows = $st->fetchAll() ?: [];
            if ($stockTypeFilter !== null) {
                $rows = array_values(array_filter($rows, static function ($row) use ($stockTypeFilter): bool {
                    return normalizeStockType((string)($row['stock_type'] ?? '')) === $stockTypeFilter;
                }));
            }
            $stockDefinitionRows = $rows;
        }
        if ($sub === 'stock-depots') {
            $st = $tenant->prepare(
                "SELECT d.id,d.name,
                        COALESCE(SUM(CASE WHEN s.stock_type='Product' THEN s.quantity ELSE 0 END),0) qty_product,
                        COALESCE(SUM(CASE WHEN s.stock_type='Ziynet' THEN s.quantity ELSE 0 END),0) qty_ziynet,
                        COALESCE(SUM(CASE WHEN s.stock_type='Scrap' THEN s.quantity ELSE 0 END),0) qty_scrap
                 FROM depots d
                 LEFT JOIN stock_items s ON s.depot_id=d.id AND s.profile_id=d.profile_id AND s.is_deleted=0
                 WHERE d.profile_id=:pid AND d.is_deleted=0
                 GROUP BY d.id,d.name
                 ORDER BY d.name"
            );
            $st->execute([':pid' => $profileId]);
            $depotStockRows = $st->fetchAll() ?: [];
            if (($user['role'] ?? '') !== 'admin') {
                $depotStockRows = array_values(array_filter($depotStockRows, static function ($row) use ($user): bool {
                    $depotId = trim((string)($row['id'] ?? ''));
                    return $depotId !== '' && canAccessDepot($user, $depotId);
                }));
            }
        }
        if (in_array($sub, ['stock-categories', 'stock-def-product', 'stock-def-scrap', 'stock-def-ziynet'], true)) {
            $st = $tenant->prepare("SELECT id,name FROM categories WHERE profile_id=:pid ORDER BY name ASC");
            $st->execute([':pid' => $profileId]);
            $stockCategoryRows = $st->fetchAll() ?: [];
        }
        if ($isCustomerAccountsSub) {
            $stTaxes = $tenant->prepare("SELECT id,name,rate,type,active FROM taxes WHERE profile_id=:pid AND active=1 ORDER BY name ASC");
            $stTaxes->execute([':pid' => $profileId]);
            $activeTaxRows = $stTaxes->fetchAll() ?: [];
        }

        if ($isCustomerAccountsSub && $selectedPerson !== '') {
            $ds = $tenant->prepare("SELECT * FROM debt_records WHERE profile_id=:pid AND person_full_name=:pn AND is_deleted=0 ORDER BY transaction_date DESC,created_at DESC");
            $ds->execute([':pid' => $profileId, ':pn' => $selectedPerson]);
            foreach ($ds->fetchAll() as $r) {
                if (($r['record_type'] ?? '') === 'Receivable') {
                    $debtsReceivable[] = $r;
                } elseif (($r['record_type'] ?? '') === 'Payable') {
                    $debtsPayable[] = $r;
                }
            }

            $hs = $tenant->prepare("SELECT * FROM customer_history_events WHERE profile_id=:pid AND person_full_name=:pn ORDER BY event_date DESC,created_at DESC");
            $hs->execute([':pid' => $profileId, ':pn' => $selectedPerson]);
            $historyRows = $hs->fetchAll() ?: [];
            $personBalanceRows = parseHistoryBalances($historyRows, $SUPPORTED_CURRENCIES);
            $historyRowsWithBalance = historyRowsWithRunning($historyRows, $SUPPORTED_CURRENCIES);
            $editEventId = trim((string)($_GET['edit_event_id'] ?? ''));
            if ($editEventId !== '') {
                foreach ($historyRows as $row) {
                    if ((string)($row['id'] ?? '') === $editEventId) {
                        $historyEditRow = $row;
                        break;
                    }
                }
            }
        }

        if ($sub === 'services-jobs') {
            $serviceJobFilters = [
                'status' => trim((string)($_GET['service_status'] ?? '')),
                'definition_id' => trim((string)($_GET['service_definition_id'] ?? '')),
                'customer' => trim((string)($_GET['service_customer'] ?? '')),
            ];
            $stDef = $tenant->prepare("SELECT id,name,custom_columns_json,clone_columns_json FROM service_definitions WHERE profile_id=:pid ORDER BY name ASC");
            $stDef->execute([':pid' => $profileId]);
            $serviceDefinitionRows = $stDef->fetchAll() ?: [];
            foreach ($serviceDefinitionRows as $defRow) {
                $defKey = trim((string)($defRow['id'] ?? ''));
                if ($defKey !== '') {
                    $serviceDefinitionMap[$defKey] = $defRow;
                }
            }
            $serviceJobSql = "SELECT j.*, d.name service_name, d.custom_columns_json, d.clone_columns_json
                 FROM service_jobs j
                 LEFT JOIN service_definitions d ON d.id=j.service_def_id AND d.profile_id=j.profile_id
                 WHERE j.profile_id=:pid";
            $serviceJobParams = [':pid' => $profileId];
            if ($serviceJobFilters['status'] !== '') {
                $serviceJobSql .= " AND j.status=:status";
                $serviceJobParams[':status'] = $serviceJobFilters['status'];
            }
            if ($serviceJobFilters['definition_id'] !== '') {
                $serviceJobSql .= " AND j.service_def_id=:definition_id";
                $serviceJobParams[':definition_id'] = $serviceJobFilters['definition_id'];
            }
            if ($serviceJobFilters['customer'] !== '') {
                $serviceJobSql .= " AND j.customer_name LIKE :customer";
                $serviceJobParams[':customer'] = '%' . $serviceJobFilters['customer'] . '%';
            }
            $serviceJobSql .= " ORDER BY FIELD(j.status, 'Islemde', 'Hazir', 'Alindi', 'Teslim Edildi'), j.start_date DESC, j.customer_name ASC";
            $stJob = $tenant->prepare($serviceJobSql);
            $stJob->execute($serviceJobParams);
            $serviceJobRows = $stJob->fetchAll() ?: [];
            foreach ($serviceJobRows as &$serviceJobRow) {
                $dynamicPayload = serviceDecodeDynamicPayload((string)($serviceJobRow['dynamic_data_json'] ?? ''));
                $serviceJobRow['dynamic_payload'] = $dynamicPayload;
                $serviceJobRow['dynamic_preview'] = serviceDynamicPreview($dynamicPayload);
                $serviceJobRow['payment_summary'] = servicePaymentSummary($dynamicPayload, (float)($serviceJobRow['fee'] ?? 0));
            }
            unset($serviceJobRow);
        }

        if ($sub === 'services-repair') {
            $stRep = $tenant->prepare("SELECT * FROM repair_records WHERE profile_id=:pid AND is_deleted=0 ORDER BY receipt_date DESC, created_at DESC");
            $stRep->execute([':pid' => $profileId]);
            $repairRows = $stRep->fetchAll() ?: [];
        }

        if ($sub === 'tools-agenda') {
            $stAgenda = $tenant->prepare("SELECT note_key,note_value FROM agenda_notes WHERE profile_id=:pid ORDER BY note_key DESC LIMIT 500");
            $stAgenda->execute([':pid' => $profileId]);
            $agendaRows = [];
            foreach ($stAgenda->fetchAll() ?: [] as $agendaRow) {
                $agendaRow['note_payload'] = agendaDecodeNote((string)($agendaRow['note_value'] ?? ''));
                $agendaRows[] = $agendaRow;
            }
        }

        if ($sub === 'tools-zakat') {
            $displayCurrencyRow = $tenant->prepare("SELECT setting_value FROM app_settings WHERE profile_id=:pid AND setting_key='display_currency' LIMIT 1");
            $displayCurrencyRow->execute([':pid' => $profileId]);
            $zakatDisplayCurrency = strtoupper(trim((string)($displayCurrencyRow->fetchColumn() ?: 'TRY')));
            if (!in_array($zakatDisplayCurrency, $SUPPORTED_CURRENCIES, true)) {
                $zakatDisplayCurrency = 'TRY';
            }
            $zakatGuestTargetCurrency = strtoupper(trim((string)($_GET['zakat_guest_currency'] ?? $zakatDisplayCurrency)));
            if (!in_array($zakatGuestTargetCurrency, $SUPPORTED_CURRENCIES, true)) {
                $zakatGuestTargetCurrency = $zakatDisplayCurrency;
            }
            $zakatStoreDate = normDate((string)($_GET['zakat_nisap_date'] ?? $zakatStoreDate));
            $zakatGuestDebt = trim((string)($_GET['zakat_guest_debt'] ?? ''));
            $zakatGuestDebtCurrency = strtoupper(trim((string)($_GET['zakat_guest_debt_currency'] ?? 'TRY')));
            if (!in_array($zakatGuestDebtCurrency, $SUPPORTED_CURRENCIES, true)) {
                $zakatGuestDebtCurrency = 'TRY';
            }
            $guestTypes = $_GET['zakat_guest_type'] ?? [];
            $guestDetails = $_GET['zakat_guest_detail'] ?? [];
            $guestAmounts = $_GET['zakat_guest_amount'] ?? [];
            if (is_array($guestTypes) && is_array($guestDetails) && is_array($guestAmounts)) {
                foreach ($guestTypes as $idx => $guestTypeRaw) {
                    $guestType = strtolower(trim((string)$guestTypeRaw));
                    $guestDetail = trim((string)($guestDetails[$idx] ?? ''));
                    $guestAmountRaw = trim((string)($guestAmounts[$idx] ?? ''));
                    $zakatGuestItems[] = [
                        'type' => $guestType,
                        'detail' => $guestDetail,
                        'amount_raw' => $guestAmountRaw,
                    ];
                }
            }
            if (!$zakatGuestItems) {
                $zakatGuestItems[] = ['type' => 'cash', 'detail' => 'TRY', 'amount_raw' => ''];
            }
            $zakatRates = fetchDashboardRates($tenant, $profileId);
            if (hasPerm($user, 'perm_zakat')) {
                $zakatStoreReport = golder_zakat_calculate_store_snapshot(
                    $tenant,
                    $profileId,
                    $zakatRates,
                    $zakatDisplayCurrency,
                    $zakatStoreDate
                );
            }
            if (hasPerm($user, 'perm_zakat_guest')) {
                $guestItemsPayload = [];
                $hasGuestInput = false;
                foreach ($zakatGuestItems as $guestItem) {
                    $amount = parseAmount((string)($guestItem['amount_raw'] ?? '0'));
                    if ($amount > 0) {
                        $hasGuestInput = true;
                    }
                    $guestItemsPayload[] = [
                        'type' => trim((string)($guestItem['type'] ?? 'cash')),
                        'detail' => trim((string)($guestItem['detail'] ?? '')),
                        'amount' => $amount,
                    ];
                }
                if ($hasGuestInput || trim($zakatGuestDebt) !== '' || isset($_GET['zakat_guest_submit'])) {
                    $zakatGuestResult = golder_zakat_calculate_guest(
                        $zakatRates,
                        $guestItemsPayload,
                        parseAmount($zakatGuestDebt !== '' ? $zakatGuestDebt : '0'),
                        $zakatGuestDebtCurrency,
                        $zakatGuestTargetCurrency
                    );
                }
            }
        }

        if ($sub === 'tools-search') {
            $qSearch = trim((string)($_GET['q'] ?? ''));
            if ($qSearch !== '') {
                $like = '%' . $qSearch . '%';
                $st1 = $tenant->prepare("SELECT 'Musteri' src_type, full_name src_name, phone_number src_detail, id src_id FROM persons WHERE profile_id=:pid AND is_deleted=0 AND (full_name LIKE :q1 OR phone_number LIKE :q2) LIMIT 60");
                $st1->execute([':pid' => $profileId, ':q1' => $like, ':q2' => $like]);
                $toolSearchRows = array_merge($toolSearchRows, $st1->fetchAll() ?: []);

                $st2 = $tenant->prepare("SELECT 'Stok Tanim' src_type, name src_name, stock_type src_detail, id src_id FROM product_definitions WHERE profile_id=:pid AND is_deleted=0 AND name LIKE :q LIMIT 60");
                $st2->execute([':pid' => $profileId, ':q' => $like]);
                $toolSearchRows = array_merge($toolSearchRows, $st2->fetchAll() ?: []);

                $st3 = $tenant->prepare("SELECT 'Barkod' src_type, unique_barcode src_name, CONCAT(item_type_name,' / ',stock_type) src_detail, id src_id FROM stock_items WHERE profile_id=:pid AND is_deleted=0 AND unique_barcode<>'' AND unique_barcode LIKE :q LIMIT 60");
                $st3->execute([':pid' => $profileId, ':q' => $like]);
                $toolSearchRows = array_merge($toolSearchRows, $st3->fetchAll() ?: []);

                $st4 = $tenant->prepare("SELECT 'Tamir' src_type, customer_full_name src_name, product_description src_detail, id src_id FROM repair_records WHERE profile_id=:pid AND is_deleted=0 AND (customer_full_name LIKE :q1 OR product_description LIKE :q2) LIMIT 60");
                $st4->execute([':pid' => $profileId, ':q1' => $like, ':q2' => $like]);
                $toolSearchRows = array_merge($toolSearchRows, $st4->fetchAll() ?: []);
            }
        }

        if (in_array($sub, ['stock-product', 'stock-scrap', 'stock-ziynet'], true)) {
            $barcodeSearch = trim((string)($_GET['barcode'] ?? ''));
            if ($barcodeSearch !== '') {
                $likeBarcode = '%' . $barcodeSearch . '%';
                $stBarcode = $tenant->prepare(
                    "SELECT s.id, s.depot_id, s.unique_barcode, s.item_type_name, s.stock_type, s.quantity, s.quantity_gr,
                            COALESCE(d.name,'Depo') depot_name
                     FROM stock_items s
                     LEFT JOIN depots d ON d.id=s.depot_id AND d.profile_id=s.profile_id
                     WHERE s.profile_id=:pid AND s.is_deleted=0 AND s.unique_barcode<>'' AND s.unique_barcode LIKE :q
                     ORDER BY s.updated_at DESC, s.created_at DESC
                     LIMIT 200"
                );
                $stBarcode->execute([':pid' => $profileId, ':q' => $likeBarcode]);
                $barcodeRows = $stBarcode->fetchAll() ?: [];
                if (($user['role'] ?? '') !== 'admin') {
                    $filtered = [];
                    foreach ($barcodeRows as $r) {
                        $depotId = trim((string)($r['depot_id'] ?? ''));
                        if ($depotId !== '' && !canAccessDepot($user, $depotId)) {
                            continue;
                        }
                        $filtered[] = $r;
                    }
                    $barcodeRows = $filtered;
                }
                $stockBarcodeRows = $barcodeRows;
            }
        }

        if ($sub === 'tools-reports') {
            $reportType = trim((string)($_GET['report_type'] ?? 'cashflow'));
            $reportStart = normDate((string)($_GET['report_start'] ?? date('Y-m-01')));
            $reportEnd = normDate((string)($_GET['report_end'] ?? date('Y-m-d')));
            $reportCustomer = trim((string)($_GET['report_customer'] ?? ''));
            $reportDepot = trim((string)($_GET['report_depot'] ?? ''));
            $dataset = buildReportDataset(
                $tenant,
                $profileId,
                $reportType,
                $reportStart,
                $reportEnd,
                $SUPPORTED_CURRENCIES,
                $reportCustomer,
                $reportDepot,
                $depotNames
            );
            $toolReportHeaders = is_array($dataset['headers'] ?? null) ? $dataset['headers'] : [];
            $toolReportRows = is_array($dataset['rows'] ?? null) ? $dataset['rows'] : [];
            $toolReportSummary = is_array($dataset['summary'] ?? null) ? $dataset['summary'] : [];
        }

        if (in_array($sub, ['settings-general', 'settings-security', 'settings-users', 'settings-taxes', 'settings-transfer', 'settings-devices'], true)) {
            $stSet = $tenant->prepare("SELECT setting_key,setting_value FROM app_settings WHERE profile_id=:pid");
            $stSet->execute([':pid' => $profileId]);
            foreach ($stSet->fetchAll() ?: [] as $r) {
                $generalSettings[(string)($r['setting_key'] ?? '')] = (string)($r['setting_value'] ?? '');
            }
            $stUsers = $tenant->prepare("SELECT id,username,full_name,role,is_active FROM users WHERE profile_id=:pid AND is_deleted=0 ORDER BY role DESC, full_name ASC");
            $stUsers->execute([':pid' => $profileId]);
            $settingsUsers = $stUsers->fetchAll() ?: [];
            $stProfiles = $tenant->prepare("SELECT id,name FROM profiles WHERE is_deleted=0 ORDER BY name ASC");
            $stProfiles->execute();
            $profileOptionsAll = $stProfiles->fetchAll() ?: [];
            $stDepots = $tenant->prepare("SELECT id,name FROM depots WHERE profile_id=:pid AND is_deleted=0 ORDER BY name ASC");
            $stDepots->execute([':pid' => $profileId]);
            $settingsDepotOptions = $stDepots->fetchAll() ?: [];
            $stAccOpts = $tenant->prepare("SELECT id,name,type FROM financial_accounts WHERE profile_id=:pid AND is_deleted=0 AND type IN ('cash','card') ORDER BY type,name");
            $stAccOpts->execute([':pid' => $profileId]);
            foreach ($stAccOpts->fetchAll() ?: [] as $accRow) {
                $accType = (string)($accRow['type'] ?? '');
                if ($accType === 'cash') {
                    $settingsCashAccountOptions[] = $accRow;
                } elseif ($accType === 'card') {
                    $settingsCardAccountOptions[] = $accRow;
                }
            }
            if ($sub === 'settings-taxes') {
                $stTaxDef = $tenant->prepare("SELECT id,name,rate,type,country,active,created_at FROM taxes WHERE profile_id=:pid ORDER BY name ASC");
                $stTaxDef->execute([':pid' => $profileId]);
                $taxDefinitionRows = $stTaxDef->fetchAll() ?: [];

                $stTaxProfiles = $tenant->prepare("SELECT id,name,description,created_at FROM tax_profiles WHERE profile_id=:pid ORDER BY name ASC");
                $stTaxProfiles->execute([':pid' => $profileId]);
                $taxProfileRows = $stTaxProfiles->fetchAll() ?: [];

                $stTaxItems = $tenant->prepare(
                    "SELECT i.id, i.tax_profile_id, i.tax_id, i.priority, i.enabled_by_default,
                            COALESCE(t.name,'-') tax_name, COALESCE(t.type,'percent') tax_type, COALESCE(t.rate,0) tax_rate
                     FROM tax_profile_items i
                     LEFT JOIN taxes t ON t.id=i.tax_id AND t.profile_id=i.profile_id
                     WHERE i.profile_id=:pid
                     ORDER BY i.tax_profile_id ASC, i.priority ASC, t.name ASC"
                );
                $stTaxItems->execute([':pid' => $profileId]);
                foreach ($stTaxItems->fetchAll() ?: [] as $itemRow) {
                    $pId = trim((string)($itemRow['tax_profile_id'] ?? ''));
                    if ($pId === '') {
                        continue;
                    }
                    $taxProfileItemsMap[$pId][] = $itemRow;
                }

                $stLedger = $tenant->prepare(
                    "SELECT id,source_table,source_id,currency,subtotal,tax_total,grand_total,snapshot_json,created_at
                     FROM tax_applications
                     WHERE profile_id=:pid
                     ORDER BY created_at DESC
                     LIMIT 100"
                );
                $stLedger->execute([':pid' => $profileId]);
                $taxLedgerRows = $stLedger->fetchAll() ?: [];
            }

            if ($sub === 'settings-devices') {
                $customerId = (int)($_SESSION['customer']['id'] ?? 0);
                if ($customerId > 0) {
                    $master = golder_master_pdo();
                    $settingsDeviceRows = golder_list_customer_devices($master, $customerId);
                    $settingsWebSessionRows = golder_list_customer_web_sessions($master, $customerId);
                }
            }


        }
    } catch (Throwable $ex) {
        $tenantError = 'Veriler yüklenemedi: ' . $ex->getMessage();
    }
}
$webSessionPayload = golder_current_web_session_payload();
if (!empty($_SESSION['customer']) && trim((string)($webSessionPayload['session_id'] ?? '')) !== '') {
    $webSessionClient = [
        'sessionId' => (string)($webSessionPayload['session_id'] ?? ''),
        'heartbeatUrl' => 'api/session/heartbeat.php',
        'disconnectUrl' => 'api/session/disconnect.php',
        'heartbeatIntervalSeconds' => (int)($webSessionPayload['heartbeat_interval_seconds'] ?? golder_web_session_heartbeat_interval_seconds()),
    ];
}
?>
<!doctype html>
<html lang="<?= e(strtolower(web_lang_code())) ?>">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><?= e(ui_text('web_title', 'Golder Web Yönetim', 'Golder Web Management')) ?></title>
       
    <!-- 1. Ana ekranda görÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€Â¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã…Â¡ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¼necek ikon (Sunucuya attıÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€šÃ‚ÂÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â¦ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¸ınız resim) -->
    <link rel="apple-touch-icon" href="apple-touch-icon.png">
    
    <!-- 2. Tarayıcı adres çubuÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€šÃ‚ÂÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â¦ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¸unu gizler (App hissi verir) -->
    <meta name="apple-mobile-web-app-capable" content="yes">
    
    <!-- 3. ÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã¢â‚¬Â ÃƒÂ¢Ã¢â€šÂ¬Ã¢â€Â¢ÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â¦ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€¦Ã¢â‚¬Å“st durum çubuÃƒÆ’Ã†â€™Ãƒâ€ Ã¢â‚¬â„¢ÃƒÆ’Ã‚Â¢ÃƒÂ¢Ã¢â‚¬Å¡Ã‚Â¬Ãƒâ€šÃ‚ÂÃƒÆ’Ã†â€™ÃƒÂ¢Ã¢â€šÂ¬Ã‚Â¦ÃƒÆ’Ã¢â‚¬Å¡Ãƒâ€šÃ‚Â¸unun (saat, pil vb.) rengini ayarlar -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    
    <!-- 4. Ana ekranda ikonun altında yazacak kısa isim -->
    <meta name="apple-mobile-web-app-title" content="Golder">
        <style>
        :root{
            --bg:<?= e($themeVars['bg']) ?>;
            --bg2:<?= e($themeVars['bg2']) ?>;
            --card:<?= e($themeVars['card']) ?>;
            --line:<?= e($themeVars['line']) ?>;
            --text:<?= e($themeVars['text']) ?>;
            --muted:<?= e($themeVars['muted']) ?>;
            --ok:<?= e($themeVars['ok']) ?>;
            --okbg:<?= e($themeVars['okbg']) ?>;
            --err:<?= e($themeVars['err']) ?>;
            --errbg:<?= e($themeVars['errbg']) ?>;
            --primary:<?= e($themeVars['primary']) ?>;
            --primary2:<?= e($themeVars['primary2']) ?>;
            --secondary:<?= e($themeVars['secondary'] ?? $themeVars['primary2']) ?>;
            --tertiary:<?= e($themeVars['tertiary'] ?? $themeVars['primary2']) ?>;
            --bgalt:<?= e($themeVars['background_alt'] ?? $themeVars['card']) ?>;
            --text-light:<?= e($themeVars['text_light'] ?? '#FFFFFF') ?>;
            --danger:<?= e($themeVars['danger'] ?? $themeVars['err']) ?>;
            --warning:<?= e($themeVars['warning'] ?? $themeVars['primary2']) ?>;
        }
    </style>
    <link rel="stylesheet" href="assets/css/app.css?v=1.19.0">
    
</head>
<body>
<div class="app">
    <div class="top">
        <div>
            <div class="ttl"><?= e(ui_text('web_title', 'Golder Web Yönetim', 'Golder Web Management')) ?></div>
            <div class="meta">
                <?php if (!empty($_SESSION['customer'])): ?>
                    <?= e(ui_text('customer_label', 'Müşteri', 'Customer')) ?>: <strong><?= e($_SESSION['customer']['username'] ?? '') ?></strong>
                    <?php if (!empty($_SESSION['profile'])): ?> | <?= e(ui_text('profile_label', 'Profil', 'Profile')) ?>: <strong><?= e($_SESSION['profile']['name'] ?? '') ?></strong><?php endif; ?>
                    <?php if (!empty($_SESSION['user'])): ?> | <?= e(ui_text('user', 'Kullanıcı', 'User')) ?>: <strong><?= e($_SESSION['user']['full_name'] ?? '') ?></strong> (<?= e(role_label((string)($_SESSION['user']['role'] ?? ''))) ?>)<?php endif; ?>
                <?php else: ?>
                    <?= e(ui_text('waiting_customer_login', 'Müşteri girişi bekleniyor.', 'Waiting for customer login.')) ?>
                <?php endif; ?>
            </div>
        </div>
        <div class="acts">
            <?php if (!empty($_SESSION['customer']) && !empty($_SESSION['user'])): ?>
                <form class="inline" method="post"><input type="hidden" name="action" value="change_profile"><button type="submit"><?= e(ui_text('switch_profile', 'Profil Değiştir', 'Change Profile')) ?></button></form>
                <form class="inline" method="post"><input type="hidden" name="action" value="logout_user"><button type="submit"><?= e(ui_text('logout_user', 'Kullanıcı Çıkışı', 'Logout User')) ?></button></form>
            <?php endif; ?>
            <?php if (!empty($_SESSION['customer'])): ?>
                <form class="inline" method="post"><input type="hidden" name="action" value="logout_all"><button type="submit" class="btnd"><?= e(ui_text('logout_all', 'Tam Çıkış', 'Full Logout')) ?></button></form>
            <?php endif; ?>
        </div>
    </div>

    <?php if ($flash): ?><div class="flash <?= e(($flash['type'] ?? '') === 'ok' ? 'ok' : 'err') ?>"><?= e($flash['msg'] ?? '') ?></div><?php endif; ?>
    <?php if ($error !== ''): ?><div class="flash err"><?= e($error) ?></div><?php endif; ?>
    <?php if ($tenantError !== ''): ?><div class="flash err"><?= e($tenantError) ?></div><?php endif; ?>
    <?php if (!empty($DEBUG_MODE)): ?>
        <div class="flash" style="background:#fff8e1;color:#5d4037;border:1px solid #ffcc80">
            <strong>DEBUG</strong> |
            act=<?= e($act) ?> |
            cat=<?= e($cat) ?> |
            sub=<?= e($sub) ?> |
            customer=<?= !empty($_SESSION['customer']) ? '1' : '0' ?> |
            profile=<?= !empty($_SESSION['profile']) ? '1' : '0' ?> |
            user=<?= !empty($_SESSION['user']) ? '1' : '0' ?>
        </div>
    <?php endif; ?>

    <?php if (empty($_SESSION['customer'])): ?>
        <section class="panel pairing-panel">
            <div class="pairing-logo-wrap">
                <img src="Golder.png" alt="Golder Logo" class="pairing-logo">
            </div>
            <h2><?= e(ui_text('device_pairing', 'Cihaz Eşleştirme', 'Device Pairing')) ?></h2>
            <p class="muted pairing-help">
                <?= e(ui_text('pairing_help', 'Bilgisayarınızdaki Golder programından Ayarlar > Web Cihazları menüsüne girin ve "Yeni Cihaz Ekle" butonuna basın.', 'Open Settings > Web Devices in Golder on your computer and click "Add New Device".')) ?>
            </p>
            
            <!-- Kamera Görüntüsünün Çıkacağı Kutu -->
            <div id="reader" class="pairing-reader"></div>
            
            <button type="button" id="start_camera_btn" class="btnp pairing-btn"><?= e(ui_text('start_camera_qr', 'Kamerayı Aç ve QR Okut', 'Open Camera and Scan QR')) ?></button>

            <div class="sep"><?= e(ui_text('manual_or', 'veya manuel olarak', 'or manually')) ?></div>

            <form method="post" class="fg pairing-form" id="pair_form">
                <input type="hidden" name="action" value="device_pair">
                <input type="hidden" name="pairing_token" id="pairing_token_input">
                <input type="hidden" name="hardware_id" id="hardware_id_input">
                <input type="hidden" name="device_name" id="device_name_input">
                
                <div class="f">
                    <input type="text" name="pin_code" id="pin_code_input" class="pairing-code-input" placeholder="<?= e(ui_text('enter_six_digit_code', 'Altı haneli kodu girin', 'Enter the six-digit code')) ?>" maxlength="6">
                </div>
                <div class="f">
                    <button class="btnp pairing-submit" type="submit" id="pair_submit_btn"><?= e(ui_text('confirm_code_and_enter', 'Kodu Onayla ve Gir', 'Confirm Code and Enter')) ?></button>
                </div>
            </form>
        </section>

        <!-- HTML5-QRCode Kütüphanesi (Kamerayı tarayıcı içinde çalıştırır) -->
        <script src="https://unpkg.com/html5-qrcode"></script>
        
        <script>
            // 1. Cihaza Kalıcı Kimlik Atama (LocalStorage Persistent Hardware ID)
            let hwId = localStorage.getItem('golder_web_hw_id');
            if (!hwId) {
                hwId = 'GLD-' + Math.random().toString(36).substring(2, 10).toUpperCase();
                localStorage.setItem('golder_web_hw_id', hwId);
            }
            document.getElementById('hardware_id_input').value = hwId;

            // 2. Cihaz İsmi Belirleme (Örn: iPhone Tarayıcı)
            let devName = localStorage.getItem('golder_web_dev_name');
            if (!devName) {
                const ua = navigator.userAgent.toLowerCase();
                if (ua.includes('iphone') || ua.includes('ipad')) devName = 'iOS Cihaz';
                else if (ua.includes('android')) devName = 'Android Cihaz';
                else if (ua.includes('mac')) devName = 'Mac Tarayıcı';
                else if (ua.includes('windows')) devName = 'Windows Tarayıcı';
                else devName = 'Mobil Tarayıcı';
                localStorage.setItem('golder_web_dev_name', devName);
            }
            document.getElementById('device_name_input').value = devName;
            document.cookie = `golder_web_dev_name=${encodeURIComponent(devName)}; path=/; max-age=${60 * 60 * 24 * 3650}`;

            function askDeviceName(defaultName) {
                const raw = prompt(
                    <?= json_encode(ui_text('ask_device_name', 'Bu cihazın ismi nedir?', 'What is the name of this device?'), JSON_UNESCAPED_UNICODE) ?>,
                    defaultName || <?= json_encode(ui_text('web_device_name_default', 'Web Cihazı', 'Web Device'), JSON_UNESCAPED_UNICODE) ?>
                );
                if (raw === null) return null;
                const v = raw.trim();
                const finalName = v !== '' ? v : (defaultName || <?= json_encode(ui_text('web_device_name_default', 'Web Cihazı', 'Web Device'), JSON_UNESCAPED_UNICODE) ?>);
                localStorage.setItem('golder_web_dev_name', finalName);
                document.getElementById('device_name_input').value = finalName;
                document.cookie = `golder_web_dev_name=${encodeURIComponent(finalName)}; path=/; max-age=${60 * 60 * 24 * 3650}`;
                return finalName;
            }

            // 3. Kamera ile QR Okuma Mantığı
            const html5QrCode = new Html5Qrcode("reader");
            const startBtn = document.getElementById('start_camera_btn');
            const pairForm = document.getElementById('pair_form');

            startBtn.addEventListener('click', () => {
                document.getElementById('reader').style.display = "block";
                startBtn.style.display = "none";

                html5QrCode.start(
                    { facingMode: "environment" }, // Arka kamerayı kullan
                    {
                        fps: 10,
                        qrbox: { width: 250, height: 250 }
                    },
                    (decodedText) => {
                        // QR başarıyla okunduğunda tetiklenir
                        html5QrCode.stop().then(() => {
                            const named = askDeviceName(devName);
                            if (named === null) {
                                document.getElementById('reader').style.display = "none";
                                startBtn.style.display = "block";
                                return;
                            }
                            devName = named;
                            // Okunan şifreli token'ı gizli inputa yaz ve formu otomatik post et
                            document.getElementById('pairing_token_input').value = decodedText;
                            pairForm.submit();
                        });
                    },
                    (errorMessage) => {
                        // Okunurken geçen boş kareler, görmezden gel.
                    }
                ).catch((err) => {
                    alert(<?= json_encode(ui_text('camera_access_error', 'Kameraya erişilemedi! Lütfen tarayıcı ayarlarından kamera izni verin veya kodu manuel girin.', 'Camera access failed. Please allow camera access in your browser settings or enter the code manually.'), JSON_UNESCAPED_UNICODE) ?>);
                    document.getElementById('reader').style.display = "none";
                    startBtn.style.display = "block";
                });
            });

            pairForm.addEventListener('submit', (e) => {
                if (document.getElementById('pairing_token_input').value) {
                    return;
                }
                const named = askDeviceName(devName);
                if (named === null) {
                    e.preventDefault();
                    return;
                }
                devName = named;
            });
        </script>
    <?php elseif (empty($_SESSION['profile'])): ?>
        <section class="panel">
            <h2><?= e(ui_text('select_profile', 'Profil Seçimi', 'Select Profile')) ?></h2>
            <p class="muted"><?= e(ui_text('select_profile_flow', 'Profil -> kullanıcı -> kategori akışı ile ilerlenir.', 'Continue with profile -> user -> category flow.')) ?></p>
            <form method="post" class="row">
                <input type="hidden" name="action" value="select_profile">
                <select name="profile_id" required style="max-width:420px">
                    <option value=""><?= e(ui_text('select_profile', 'Profil seçin', 'Select profile')) ?>...</option>
                    <?php foreach ($profiles as $p): ?>
                        <option value="<?= e($p['id']) ?>"><?= e($p['name']) ?> <?= ((int)($p['is_active'] ?? 0) === 1 ? '' : '(Pasif)') ?></option>
                    <?php endforeach; ?>
                </select>
                <button class="btnp" type="submit"><?= e(ui_text('open_profile', 'Profili Aç', 'Open Profile')) ?></button>
            </form>
            <div class="sep"></div>
            <h3><?= e(ui_text('create_profile', 'Yeni Profil Oluştur', 'Create Profile')) ?></h3>
            <form method="post" class="row">
                <input type="hidden" name="action" value="create_profile">
                <input type="text" name="profile_name" required placeholder="<?= e(ui_text('profile_name_ph', 'Profil adı...', 'Profile name...')) ?>" style="max-width:420px">
                <button type="submit"><?= e(ui_text('create_profile', 'Profil Oluştur', 'Create Profile')) ?></button>
            </form>
        </section>
    <?php elseif (empty($_SESSION['user'])): ?>
        <section class="panel">
            <h2><?= e(ui_text('user_login', 'Profil Kullanıcı Girişi', 'Profile User Login')) ?></h2>
            <p class="muted"><?= e(ui_text('login_hint', 'Kullanıcıyı seçin. Personel yok ve profil şifresi kapalıysa şifre boş bırakılabilir.', 'Select a user. If there is no staff and profile password is disabled, it can be left blank.')) ?></p>
            <form method="post" class="fg">
                <input type="hidden" name="action" value="user_login">
                <div class="f">
                    <label><?= e(ui_text('user', 'Kullanıcı', 'User')) ?></label>
                    <select name="profile_username" required>
                        <option value=""><?= e(ui_text('user', 'Kullanıcı', 'User')) ?>...</option>
                        <?php foreach ($users as $u): ?>
                            <option value="<?= e($u['username']) ?>"><?= e(($u['full_name'] ?? $u['username']) . ' (' . role_label((string)($u['role'] ?? '')) . ')') ?></option>
                        <?php endforeach; ?>
                    </select>
                </div>
                <div class="f"><label><?= e(ui_text('password', 'Şifre', 'Password')) ?></label><input type="password" name="profile_password" placeholder="<?= e(ui_text('password_optional_ph', 'Şifresiz profilde boş bırakabilirsiniz', 'Leave blank for passwordless profiles')) ?>"></div>
                <div class="f"><label>&nbsp;</label><button type="submit" class="btnp"><?= e(ui_text('login_btn', 'Giriş Yap', 'Login')) ?></button></div>
            </form>
        </section>
    <?php else: ?>
        <?php
        $u = $_SESSION['user'];
        if (!isset($cats[$cat])) {
            $cat = '';
            $sub = '';
        }
        if ($cat !== '' && $sub !== '') {
            $allowed = false;
            foreach ($cats[$cat]['subs'] as $s) {
                if ($s['id'] !== $sub) {
                    continue;
                }
                if (isset($s['perm']) && hasPerm($u, $s['perm'])) {
                    $allowed = true;
                }
                if (isset($s['any']) && hasAnyPerm($u, $s['any'])) {
                    $allowed = true;
                }
                break;
            }
            if (!$allowed) {
                $cat = '';
                $sub = '';
            }
        }
        if ($cat === ''):
        ?>
            <section class="panel dashboard-home">
                <?php
                $dashboardLogoPath = __DIR__ . DIRECTORY_SEPARATOR . 'Golder.png';
                $dashboardHasLogo = is_file($dashboardLogoPath);
                
                // Ayarlardan mağaza adını çek, yoksa varsayılanı kullan
                $shopNameSt = $tenant->prepare("SELECT setting_value FROM app_settings WHERE profile_id=:pid AND setting_key='shop_name' LIMIT 1");
                $shopNameSt->execute([':pid' => $profileId]);
                $shopNameRow = $shopNameSt->fetch();
                $dashboardHeroTitle = !empty($shopNameRow['setting_value']) ? trim(trim((string)$shopNameRow['setting_value'], '"')) : 'GOLDER KUYUMCU MUHASEBE PROGRAMI';
                
                $dashboardHeroMeta = dashboardNowLabel();
                ?>
                <div class="dashboard-hero<?= $dashboardHasLogo ? '' : ' dashboard-hero-no-logo' ?>">
                    <div class="dashboard-hero-copy">
                        <h1 class="dashboard-hero-title"><?= e($dashboardHeroTitle) ?></h1>
                        <div class="dashboard-hero-meta"><?= e($dashboardHeroMeta) ?></div>
                    </div>
                </div>

                <div class="dashboard-top-row">
                    <div class="dashboard-card-column dashboard-section">
                        <div class="dashboard-section-title"><?= e(ui_text('cash_accounts', 'Kasalar', 'Cash Accounts')) ?></div>
                        <div class="dashboard-cards">
                    <?php if (!$dashboardAccountCards): ?>
                        <div class="empty-box muted"><?= e(ui_text('no_accounts_to_show', 'Henüz görüntülenecek kasa veya kart hesabı bulunmuyor.', 'There are no cash or card accounts to display yet.')) ?></div>
                    <?php else: ?>
                        <?php foreach ($dashboardAccountCards as $card): ?>
                            <?php
                            $cardType = ((string)($card['account_type'] ?? '') === 'cash') ? 'cash' : 'card';
                            $balances = is_array($card['balances'] ?? null) ? $card['balances'] : [];
                            $balances = sortDashboardBalances($balances, $SUPPORTED_CURRENCIES);
                            ?>
                            <article class="dash-card">
                                <div class="dash-card-head">
                                    <div class="dash-card-head-copy">
                                        <h3><?= e((string)($card['account_name'] ?? '-')) ?></h3>
                                        <div class="dash-card-subtitle"><?= e($cardType === 'cash' ? ui_text('cash_tab', 'Nakit', 'Cash') : ui_text('card', 'Kart', 'Card')) ?></div>
                                    </div>
                                </div>
                                <div class="dash-balances">
                                    <?php if (!$balances): ?>
                                        <span class="dash-balance-line">0</span>
                                    <?php else: ?>
                                        <?php $balancePrinted = false; ?>
                                        <?php foreach ($balances as $currency => $amount): ?>
                                            <?php
                                            $bal = (float)$amount;
                                            if (abs($bal) < 0.0001) {
                                                continue;
                                            }
                                            $balancePrinted = true;
                                            $balClass = $bal >= 0 ? 'pos' : 'neg';
                                            ?>
                                            <div class="dash-balance-line <?= e($balClass) ?>">
                                                <span class="dash-balance-amount"><?= e(formatAmountByType($bal, (string)$currency, $SUPPORTED_CURRENCIES)) ?></span>
                                                <span class="dash-balance-currency"><?= e((string)$currency) ?></span>
                                            </div>
                                        <?php endforeach; ?>
                                        <?php if (!$balancePrinted): ?><span class="dash-balance-line">0</span><?php endif; ?>
                                    <?php endif; ?>
                                </div>
                            </article>
                        <?php endforeach; ?>
                    <?php endif; ?>
                        </div>
                    </div>
                    <?php if ($dashboardHasLogo): ?>
                        <div class="dashboard-logo-col">
                            <div class="dashboard-logo-wrap dashboard-logo-wrap-hero">
                                <img class="dashboard-logo" src="Golder.png" alt="Golder">
                            </div>
                        </div>
                    <?php endif; ?>
                </div>

                <div class="dashboard-grid">
                    <div class="dashboard-block">
                        <div class="block-head">
                            <h3>📋 <?= e(ui_text('recent_transactions_live', 'Son İşlemler', 'Recent Transactions')) ?></h3>
                            <span class="muted"><?= e(ui_text('date_type_amount_desc', 'Tarih / Tür / Tutar / Açıklama', 'Date / Type / Amount / Description')) ?></span>
                        </div>
                        <div class="tbl">
                            <table>
                                <thead><tr><th><?= e(ui_text('date', 'Tarih', 'Date')) ?></th><th><?= e(ui_text('type', 'Tür', 'Type')) ?></th><th><?= e(ui_text('amount', 'Tutar', 'Amount')) ?></th><th><?= e(ui_text('description', 'Açıklama', 'Description')) ?></th></tr></thead>
                                <tbody>
                                <?php if (!$dashboardRecentTransactions): ?>
                                    <tr><td colspan="4" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                                <?php else: ?>
                                    <?php foreach ($dashboardRecentTransactions as $tx): ?>
                                        <?php
                                        $rawType = trim((string)($tx['transaction_type'] ?? ''));
                                        $typeLabel = $rawType === 'Income'
                                            ? ui_text('entry', 'Giriş', 'Entry')
                                            : ($rawType === 'Expense' ? ui_text('exit', 'Çıkış', 'Exit') : $rawType);
                                        $typeClass = $rawType === 'Income' ? 'txt-pos' : ($rawType === 'Expense' ? 'txt-neg' : '');
                                        $currency = trim((string)($tx['currency'] ?? 'TRY'));
                                        $ts = strtotime((string)($tx['created_at'] ?? $tx['transaction_date'] ?? ''));
                                        $dt = $ts ? date('d.m.Y H:i', $ts) : trDate((string)($tx['transaction_date'] ?? ''));
                                        ?>
                                        <tr>
                                            <td><?= e($dt) ?></td>
                                            <td class="<?= e($typeClass) ?>"><?= e($typeLabel) ?></td>
                                            <td><?= e(formatAmountByType((float)($tx['amount'] ?? 0), $currency, $SUPPORTED_CURRENCIES) . ' ' . $currency) ?></td>
                                            <td><?= e((string)($tx['description'] ?? '')) ?></td>
                                        </tr>
                                    <?php endforeach; ?>
                                <?php endif; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>
                    <div class="dashboard-block">
                        <div class="block-head">
                            <h3>📈 <?= e(ui_text('market_rates', 'Canlı Döviz / Altın Kurları', 'Live Currency / Gold Rates')) ?></h3>
                            <?php if ($dashboardRatesUpdatedAt !== ''): ?>
                                <span class="muted"><?= e(ui_text('updated_at', 'Güncelleme', 'Updated')) ?>: <?= e($dashboardRatesUpdatedAt) ?></span>
                            <?php endif; ?>
                        </div>
                        <div class="tbl">
                            <table>
                                <thead><tr><th><?= e(ui_text('unit', 'Birim', 'Unit')) ?></th><th><?= e(ui_text('buy_price', 'Alış', 'Buy')) ?></th><th><?= e(ui_text('sell_price', 'Satış', 'Sell')) ?></th></tr></thead>
                                <tbody>
                                <?php if (!$dashboardRateRows): ?>
                                    <tr><td colspan="3" class="muted"><?= e(ui_text('rates_unavailable', 'Kur verisi alınamadı.', 'Rate data could not be fetched.')) ?></td></tr>
                                <?php else: ?>
                                    <?php foreach ($dashboardRateRows as $r): ?>
                                        <tr>
                                            <td><?= e((string)($r['name'] ?? '')) ?></td>
                                            <td><?= e((string)($r['buy'] ?? '-')) ?></td>
                                            <td><?= e((string)($r['sell'] ?? '-')) ?></td>
                                        </tr>
                                    <?php endforeach; ?>
                                <?php endif; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>
                </div>

                <h3 class="dashboard-modules-title"><?= e(ui_text('modules', 'Modüller', 'Modules')) ?></h3>
                <div class="g3 dashboard-modules-grid">
                    <?php foreach ($cats as $k => $v): ?>
                        <a class="menu" href="<?= e(toUrl(['cat' => $k])) ?>"><?= e($v['title']) ?></a>
                    <?php endforeach; ?>
                </div>
            </section>
        <?php elseif ($sub === ''): ?>
            <section class="panel">
                <div class="row" style="justify-content:space-between"><h2><?= e($cats[$cat]['title']) ?></h2><a class="btn btn-nav" href="<?= e(toUrl()) ?>">🏠 <?= e(ui_text('back_home', 'Geri (Anasayfa)', 'Back (Home)')) ?></a></div>
                <div class="g3">
                    <?php foreach ($cats[$cat]['subs'] as $s): ?>
                        <?php
                        $ok = false;
                        if (isset($s['perm'])) {
                            $ok = hasPerm($u, $s['perm']);
                        } elseif (isset($s['any'])) {
                            $ok = hasAnyPerm($u, $s['any']);
                        }
                        if (!$ok) {
                            continue;
                        }
                        ?>
                        <a class="menu" href="<?= e(toUrl(['cat' => $cat, 'sub' => $s['id']])) ?>"><?= e($s['title']) ?></a>
                    <?php endforeach; ?>
                </div>
            </section>
        <?php else: ?>
            <section class="panel">
                <?php
                $subTitle = $sub;
                foreach ($cats[$cat]['subs'] as $subItem) {
                    if (($subItem['id'] ?? '') === $sub) {
                        $subTitle = (string)($subItem['title'] ?? $sub);
                        break;
                    }
                }
                ?>
                <div class="row" style="justify-content:space-between">
                    <h2><?= e($cats[$cat]['title']) ?> / <?= e($subTitle) ?></h2>
                    <div class="row"><a class="btn btn-nav" href="<?= e(toUrl(['cat' => $cat])) ?>">◀ <?= e(ui_text('back', 'Geri', 'Back')) ?></a><a class="btn btn-nav" href="<?= e(toUrl()) ?>">🏠 <?= e(ui_text('dashboard_tab', 'Anasayfa', 'Home')) ?></a></div>
                </div>
                <?php if ($sub === 'finance-cashflow'): ?>
                    <h3><?= e(ui_text('web_finance_cashflow', 'Gelir / Gider Ekle', 'Add Income / Expense')) ?></h3>
                    <form method="post" class="fg">
                        <input type="hidden" name="action" value="add_cash_flow">
                        <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                        <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                        <div class="f">
                            <label><?= e(ui_text('transaction_type', 'İşlem Tipi', 'Transaction Type')) ?></label>
                            <select name="transaction_type" required>
                                <option value="Income"><?= e(ui_text('entry_income', 'Gelir', 'Income')) ?></option>
                                <option value="Expense"><?= e(ui_text('exit_expense', 'Gider', 'Expense')) ?></option>
                            </select>
                        </div>
                        <div class="f">
                            <label><?= e(ui_text('account_name_col_header', 'Hesap', 'Account')) ?></label>
                            <select name="account_key" required>
                                <option value=""><?= e(ui_text('select', 'Seçin', 'Select')) ?>...</option>
                                <?php foreach ($accounts as $a): ?>
                                    <option value="<?= e($a['type'] . '|' . $a['id']) ?>"><?= e((($a['type'] === 'cash') ? ui_text('prefix_cash', 'Kasa', 'Cash') : ui_text('cards_tab', 'Kart', 'Card')) . ' - ' . $a['name']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="f"><label><?= e(ui_text('amount', 'Tutar', 'Amount')) ?></label><input type="text" name="amount" required placeholder="0,00"></div>
                        <div class="f"><label><?= e(ui_text('currency', 'Para Birimi', 'Currency')) ?></label><select name="currency"><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>"><?= e($c) ?></option><?php endforeach; ?></select></div>
                        <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="transaction_date" value="<?= e(date('Y-m-d')) ?>"></div>
                        <div class="f" style="grid-column:span 3"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description" placeholder="<?= e(ui_text('web_default_description_hint', 'Boşsa varsayılan açıklama kullanılır.', 'Default description will be used if left blank.')) ?>"></div>
                        <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                    </form>

                <?php elseif ($sub === 'finance-transfer'): ?>
                    <h3><?= e(ui_text('account_transfer_tab', 'Hesaplar Arası Transfer', 'Account Transfer')) ?></h3>
                    <form method="post" class="fg">
                        <input type="hidden" name="action" value="transfer_funds">
                        <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                        <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                        <div class="f">
                            <label><?= e(ui_text('source_account', 'Kaynak Hesap', 'Source Account')) ?></label>
                            <select name="source_account_key" required>
                                <option value=""><?= e(ui_text('select', 'Seçin', 'Select')) ?>...</option>
                                <?php foreach ($accounts as $a): ?>
                                    <option value="<?= e($a['type'] . '|' . $a['id']) ?>"><?= e((($a['type'] === 'cash') ? ui_text('prefix_cash', 'Kasa', 'Cash') : ui_text('cards_tab', 'Kart', 'Card')) . ' - ' . $a['name']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="f">
                            <label><?= e(ui_text('destination_account', 'Hedef Hesap', 'Destination Account')) ?></label>
                            <select name="dest_account_key" required>
                                <option value=""><?= e(ui_text('select', 'Seçin', 'Select')) ?>...</option>
                                <?php foreach ($accounts as $a): ?>
                                    <option value="<?= e($a['type'] . '|' . $a['id']) ?>"><?= e((($a['type'] === 'cash') ? ui_text('prefix_cash', 'Kasa', 'Cash') : ui_text('cards_tab', 'Kart', 'Card')) . ' - ' . $a['name']) ?></option>
                                <?php endforeach; ?>
                            </select>
                        </div>
                        <div class="f"><label><?= e(ui_text('amount', 'Tutar', 'Amount')) ?></label><input type="text" name="amount" required></div>
                        <div class="f"><label><?= e(ui_text('currency', 'Para Birimi', 'Currency')) ?></label><select name="currency"><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>"><?= e($c) ?></option><?php endforeach; ?></select></div>
                        <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="transaction_date" value="<?= e(date('Y-m-d')) ?>"></div>
                        <div class="f" style="grid-column:span 3"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description"></div>
                        <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('web_save_transfer', 'Transferi Kaydet', 'Save Transfer')) ?></button></div>
                    </form>

                <?php elseif ($sub === 'finance-accounts'): ?>
                    <div class="g2">
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('web_add_account', 'Kasa / Kart Ekle', 'Add Cash / Card Account')) ?></h3>
                            <form method="post" class="fg compact-grid">
                                <input type="hidden" name="action" value="add_financial_account">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <div class="f">
                                    <label><?= e(ui_text('type', 'Tür', 'Type')) ?></label>
                                    <select name="account_type" required>
                                        <option value="cash"><?= e(ui_text('cash_tab', 'Nakit Kasa', 'Cash Account')) ?></option>
                                        <option value="card"><?= e(ui_text('cards_tab', 'Kart Hesabı', 'Card Account')) ?></option>
                                    </select>
                                </div>
                                <div class="f">
                                    <label><?= e(ui_text('account_name', 'Hesap Adı', 'Account Name')) ?></label>
                                    <input type="text" name="account_name" required>
                                </div>
                                <div class="f span-2">
                                    <label>&nbsp;</label>
                                    <button class="btnp" type="submit"><?= e(ui_text('web_add_account_btn', 'Hesap Ekle', 'Add Account')) ?></button>
                                </div>
                            </form>
                        </div>
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('summary', 'Özet', 'Summary')) ?></h3>
                            <p class="muted"><?= e(ui_text('web_net_balance_hint', 'Net bakiye = Gelir - Gider (Masaüstündeki SQL mantığı ile hesaplanır).', 'Net balance = Income - Expense (calculated with the desktop SQL logic).')) ?></p>
                        </div>
                    </div>
                    <div class="tbl" style="margin-top:12px">
                        <table>
                            <thead>
                                <tr>
                                    <th><?= e(ui_text('type', 'Tür', 'Type')) ?></th>
                                    <th><?= e(ui_text('report_header_account', 'Hesap', 'Account')) ?></th>
                                    <?php foreach ($allCurrencies as $cur): ?>
                                        <th style="text-align:right"><?= e($cur) ?></th>
                                    <?php endforeach; ?>
                                </tr>
                            </thead>
                            <tbody>
                            <?php if (!$pivotedAccounts): ?>
                                <tr><td colspan="<?= 2 + count($allCurrencies) ?>" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($pivotedAccounts as $acc): ?>
                                    <tr>
                                        <td><?= e($acc['type'] === 'cash' ? ui_text('prefix_cash', 'Kasa', 'Cash') : ui_text('cards_tab', 'Kart', 'Card')) ?></td>
                                        <td><strong><?= e($acc['name']) ?></strong></td>
                                        <?php foreach ($allCurrencies as $cur): ?>
                                            <?php
                                            $bal = $acc['balances'][$cur] ?? 0.0;
                                            $colorClass = $bal > 0 ? 'txt-pos' : ($bal < 0 ? 'txt-neg' : '');
                                            ?>
                                            <td style="text-align:right" class="<?= e($colorClass) ?>">
                                                <?= e(number_format($bal, 2, ',', '.')) ?>
                                            </td>
                                        <?php endforeach; ?>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif (in_array($sub, ['stock-def-product', 'stock-def-scrap', 'stock-def-ziynet'], true)): ?>
                    <?php
                    $definitionStockTypeUi = 'Product';
                    $definitionStockLabelUi = ui_text('product_def_tab', 'Altın Tanımlama', 'Gold Definitions');
                    if ($sub === 'stock-def-scrap') {
                        $definitionStockTypeUi = 'Scrap';
                        $definitionStockLabelUi = ui_text('scrap_def_tab', 'Hurda Tanımlama', 'Scrap Definitions');
                    } elseif ($sub === 'stock-def-ziynet') {
                        $definitionStockTypeUi = 'Ziynet';
                        $definitionStockLabelUi = ui_text('ziynet_def_tab', 'Ziynet Tanımlama', 'Ziynet Definitions');
                    }
                    ?>
                    <div class="g2">
                        <div class="panel" style="margin:0">
                            <h3><?= e($definitionStockLabelUi) ?></h3>
                            <form method="post" class="fg">
                                <input type="hidden" name="action" value="add_stock_definition">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <div class="f">
                                    <label><?= e(ui_text('stock_type', 'Stok Tipi', 'Stock Type')) ?></label>
                                    <input type="hidden" name="definition_stock_type" value="<?= e($definitionStockTypeUi) ?>">
                                    <input type="text" value="<?= e(stockTypeLabel($definitionStockTypeUi)) ?>" readonly>
                                </div>
                                <div class="f"><label><?= e(ui_text('definition_name', 'Tanım Adı', 'Definition Name')) ?></label><input type="text" name="definition_name" required></div>

                                <div class="f">
                                    <label><?= e(ui_text('categories', 'Kategori', 'Category')) ?></label>
                                    <select name="definition_category_id">
                                        <option value=""><?= e(ui_text('web_optional_category', 'Kategori Seçin (Opsiyonel)', 'Select Category (Optional)')) ?></option>
                                        <?php foreach ($stockCategoryRows as $cRow): ?>
                                            <option value="<?= e($cRow['id']) ?>"><?= e($cRow['name']) ?></option>
                                        <?php endforeach; ?>
                                    </select>
                                </div>

                                <div class="f"><label><?= e(ui_text('karat_detail', 'Ayar (Ops.)', 'Karat (Optional)')) ?></label><input type="text" name="definition_karat_purity" placeholder="22K"></div>
                                <div class="f"><label><?= e(ui_text('web_purchase_price_optional', 'Alış Fiyatı (Ops.)', 'Purchase Price (Optional)')) ?></label><input type="text" name="definition_default_purchase_price" placeholder="0,00"></div>
                                <div class="f"><label><?= e(ui_text('currency', 'Para Birimi', 'Currency')) ?></label><select name="definition_purchase_currency"><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>"><?= e($c) ?></option><?php endforeach; ?></select></div>

                                <?php if ($definitionStockTypeUi !== 'Scrap'): ?>
                                <div class="f"><label><?= e(ui_text('web_default_gram_optional', 'Varsayılan Gram (Ops.)', 'Default Gram (Optional)')) ?></label><input type="text" name="definition_default_weight_gr" placeholder="0,000"></div>
                                <?php endif; ?>
                                <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('add', 'Tanım Ekle', 'Add Definition')) ?></button></div>
                            </form>
                        </div>
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('info', 'Not', 'Note')) ?></h3>
                            <p class="muted"><?= e(ui_text('web_definition_note_prefix', 'Bu sekmede sadece', 'Only')) ?> <?= e(stockTypeLabel($definitionStockTypeUi)) ?> <?= e(ui_text('web_definition_note_suffix', 'tanımları listelenir.', 'definitions are listed in this tab.')) ?></p>
                        </div>
                    </div>
                    <div class="tbl" style="margin-top:12px">
                        <table>
                            <thead><tr><th><?= e(ui_text('type', 'Tip', 'Type')) ?></th><th><?= e(ui_text('name', 'Ad', 'Name')) ?></th><th><?= e(ui_text('karat_detail', 'Ayar', 'Karat')) ?></th><th><?= e(ui_text('price', 'Alış', 'Purchase')) ?></th><th><?= e(ui_text('gram', 'Gram', 'Gram')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$stockDefinitionRows): ?>
                                <tr><td colspan="6" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($stockDefinitionRows as $r): ?>
                                    <tr>
                                        <td><?= e(stockTypeLabel((string)($r['stock_type'] ?? 'Product'))) ?></td>
                                        <td><?= e($r['name'] ?? '') ?></td>
                                        <td><?= e($r['karat_purity'] ?? '-') ?></td>
                                        <td><?= e(number_format((float)($r['default_purchase_price'] ?? 0), 2, ',', '.')) ?> <?= e($r['purchase_currency'] ?? 'TRY') ?></td>
                                        <td><?= e(number_format((float)($r['default_weight_gr'] ?? 0), 3, ',', '.')) ?></td>
                                        <td>
                                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_definition', 'Tanım silinsin mi?', 'Delete definition?')) ?>');">
                                                <input type="hidden" name="action" value="delete_stock_definition">
                                                <input type="hidden" name="definition_id" value="<?= e($r['id'] ?? '') ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button type="submit" class="btnd"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'stock-depots'): ?>
                    <div class="g2">
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('web_add_depot', 'Depo Ekle', 'Add Depot')) ?></h3>
                            <form method="post" class="row">
                                <input type="hidden" name="action" value="add_depot">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <input type="text" name="depot_name" required placeholder="<?= e(ui_text('web_depot_name_ph', 'Depo adı...', 'Depot name...')) ?>" style="max-width:320px">
                                <button type="submit" class="btnp"><?= e(ui_text('web_add_depot', 'Depo Ekle', 'Add Depot')) ?></button>
                            </form>
                        </div>
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('info', 'Not', 'Note')) ?></h3>
                            <p class="muted"><?= e(ui_text('web_depot_note', 'Sıfır stoklu depolar silinebilir, stoklu depolar silinemez.', 'Only zero-stock depots can be deleted. Depots with stock cannot be deleted.')) ?></p>
                        </div>
                    </div>
                    <div class="panel" style="margin:12px 0">
                        <h3><?= e(ui_text('web_manual_depot_transfer', 'Manuel Depo Transferi', 'Manual Depot Transfer')) ?></h3>
                        <form method="post" class="fg compact-grid" id="stock-transfer-form"
                              data-product-items='<?= e(json_encode($productNames, JSON_UNESCAPED_UNICODE)) ?>'
                              data-scrap-items='<?= e(json_encode($scrapNames, JSON_UNESCAPED_UNICODE)) ?>'
                              data-ziynet-items='<?= e(json_encode($ziynetNames, JSON_UNESCAPED_UNICODE)) ?>'>
                            <input type="hidden" name="action" value="stock_transfer">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                            <div class="f">
                                <label><?= e(ui_text('source', 'Kaynak Depo', 'Source Depot')) ?></label>
                                <select name="source_depot_id" required>
                                    <option value=""><?= e(ui_text('select', 'Seçin', 'Select')) ?>...</option>
                                    <?php foreach ($depotStockRows as $dr): ?>
                                        <option value="<?= e((string)($dr['id'] ?? '')) ?>"><?= e((string)($dr['name'] ?? '')) ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="f">
                                <label><?= e(ui_text('destination', 'Hedef Depo', 'Destination Depot')) ?></label>
                                <select name="dest_depot_id" required>
                                    <option value=""><?= e(ui_text('select', 'Seçin', 'Select')) ?>...</option>
                                    <?php foreach ($depotStockRows as $dr): ?>
                                        <option value="<?= e((string)($dr['id'] ?? '')) ?>"><?= e((string)($dr['name'] ?? '')) ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="f">
                                <label><?= e(ui_text('stock_type', 'Stok Tipi', 'Stock Type')) ?></label>
                                <select name="transfer_stock_type" id="transfer_stock_type">
                                    <option value="Product"><?= e(ui_text('product_status_tab', 'Altın', 'Gold')) ?></option>
                                    <option value="Scrap"><?= e(ui_text('scrap_status_tab', 'Hurda', 'Scrap')) ?></option>
                                    <option value="Ziynet">Ziynet</option>
                                </select>
                            </div>
                            <div class="f">
                                <label><?= e(ui_text('product', 'Ürün', 'Product')) ?></label>
                                <select name="transfer_item_name" id="transfer_item_name" required>
                                    <option value=""><?= e(ui_text('select', 'Seçin', 'Select')) ?>...</option>
                                    <?php foreach ($productNames as $pn): ?>
                                        <option value="<?= e((string)$pn) ?>"><?= e((string)$pn) ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="f">
                                <label><?= e(ui_text('quantity_amount', 'Miktar', 'Quantity')) ?></label>
                                <input type="text" name="transfer_quantity" placeholder="0,000" required>
                            </div>
                            <div class="f"><label>&nbsp;</label><button type="submit" class="btnp"><?= e(ui_text('transfer', 'Transfer Et', 'Transfer')) ?></button></div>
                        </form>
                    </div>
                    <div class="tbl" style="margin-top:12px">
                        <table>
                            <thead><tr><th><?= e(ui_text('web_stock_depots', 'Depo', 'Depot')) ?></th><th><?= e(ui_text('product_status_tab', 'Altın', 'Gold')) ?></th><th><?= e(ui_text('ziynet_status_tab', 'Ziynet', 'Ziynet')) ?></th><th><?= e(ui_text('scrap_status_tab', 'Hurda', 'Scrap')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$depotStockRows): ?>
                                <tr><td colspan="5" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($depotStockRows as $r): ?>
                                    <?php
                                    $qProduct = (float)($r['qty_product'] ?? 0);
                                    $qZiynet = (float)($r['qty_ziynet'] ?? 0);
                                    $qScrap = (float)($r['qty_scrap'] ?? 0);
                                    $hasAnyStock = (abs($qProduct) > 0.0001) || (abs($qZiynet) > 0.0001) || (abs($qScrap) > 0.0001);
                                    ?>
                                    <tr>
                                        <td><?= e($r['name'] ?? '-') ?></td>
                                        <td><?= e(number_format($qProduct, 3, ',', '.')) ?></td>
                                        <td><?= e(number_format($qZiynet, 3, ',', '.')) ?></td>
                                        <td><?= e(number_format($qScrap, 3, ',', '.')) ?></td>
                                        <td>
                                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_depot', 'Depo silinsin mi?', 'Delete depot?')) ?>');">
                                                <input type="hidden" name="action" value="delete_depot">
                                                <input type="hidden" name="depot_id" value="<?= e($r['id'] ?? '') ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button type="submit" class="btnd" <?= $hasAnyStock ? 'disabled' : '' ?>><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                    <script>
                        (function () {
                            var form = document.getElementById('stock-transfer-form');
                            if (!form) return;
                            var typeSel = form.querySelector('#transfer_stock_type');
                            var itemSel = form.querySelector('#transfer_item_name');
                            if (!typeSel || !itemSel) return;
                            var maps = {
                                Product: JSON.parse(form.getAttribute('data-product-items') || '[]'),
                                Scrap: JSON.parse(form.getAttribute('data-scrap-items') || '[]'),
                                Ziynet: JSON.parse(form.getAttribute('data-ziynet-items') || '[]')
                            };
                            var syncItems = function () {
                                var k = String(typeSel.value || 'Product');
                                var rows = Array.isArray(maps[k]) ? maps[k] : [];
                                var prev = String(itemSel.value || '');
                                itemSel.innerHTML = '<option value=\"\"><?= e(ui_text('select', 'Seçin', 'Select')) ?>...</option>';
                                rows.forEach(function (name) {
                                    var v = String(name || '').trim();
                                    if (!v) return;
                                    var opt = document.createElement('option');
                                    opt.value = v;
                                    opt.textContent = v;
                                    itemSel.appendChild(opt);
                                });
                                if (prev && rows.indexOf(prev) >= 0) {
                                    itemSel.value = prev;
                                }
                            };
                            typeSel.addEventListener('change', syncItems);
                            syncItems();
                        })();
                    </script>

                <?php elseif (in_array($sub, ['stock-product', 'stock-scrap', 'stock-ziynet'], true)): ?>
                    <?php
                    $stockTypeUi = 'Product';
                    $stockTitleUi = ui_text('product_status_tab', 'Altın Stoğu', 'Gold Stock');
                    if ($sub === 'stock-scrap') {
                        $stockTypeUi = 'Scrap';
                        $stockTitleUi = ui_text('scrap_status_tab', 'Hurda Stoğu', 'Scrap Stock');
                    } elseif ($sub === 'stock-ziynet') {
                        $stockTypeUi = 'Ziynet';
                        $stockTitleUi = ui_text('ziynet_status_tab', 'Ziynet Stoğu', 'Ziynet Stock');
                    }
                    $stockTotalQtyUi = 0.0;
                    $stockTotalValueUi = 0.0;
                    foreach ($stockStatusRows as $sr) {
                        $stockTotalQtyUi += (float)($sr['total_qty'] ?? 0);
                        $stockTotalValueUi += (float)($sr['total_value_raw'] ?? 0);
                    }
                    $barcodeSearchUi = trim((string)($_GET['barcode'] ?? ''));
                    ?>
                    <h3><?= e($stockTitleUi) ?></h3>
                    <div class="g3">
                        <div class="panel" style="margin:0">
                            <div class="muted"><?= e(ui_text('web_total_items', 'Toplam Kalem', 'Total Items')) ?></div>
                            <div style="font-size:22px;font-weight:700"><?= e((string)count($stockStatusRows)) ?></div>
                        </div>
                        <div class="panel" style="margin:0">
                            <div class="muted"><?= e(ui_text('web_total_quantity', 'Toplam Miktar', 'Total Quantity')) ?></div>
                            <div style="font-size:22px;font-weight:700"><?= e(number_format($stockTotalQtyUi, 3, ',', '.')) ?></div>
                        </div>
                        <div class="panel" style="margin:0">
                            <div class="muted"><?= e(ui_text('web_total_raw_value', 'Toplam Değer (Ham)', 'Total Raw Value')) ?></div>
                            <div style="font-size:22px;font-weight:700"><?= e(number_format($stockTotalValueUi, 2, ',', '.')) ?></div>
                        </div>
                    </div>
                    <div class="tbl" style="margin-top:12px">
                        <table>
                            <thead><tr><th><?= e(ui_text('web_stock_depots', 'Depo', 'Depot')) ?></th><th><?= e(ui_text('product', 'Ürün', 'Product')) ?></th><th><?= e(ui_text('quantity_amount', 'Miktar', 'Quantity')) ?></th><th><?= e(ui_text('web_avg_purchase', 'Ort. Alış', 'Avg. Purchase')) ?></th><th><?= e(ui_text('total_value_lbl', 'Toplam Değer', 'Total Value')) ?></th><th><?= e(ui_text('barcode', 'Barkodlu', 'Barcode Count')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$stockStatusRows): ?>
                                <tr><td colspan="6" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($stockStatusRows as $r): ?>
                                    <tr>
                                        <td><?= e((string)($r['depot_name'] ?? 'Depo')) ?></td>
                                        <td><?= e((string)($r['item_type_name'] ?? '')) ?></td>
                                        <td><?= e(number_format((float)($r['total_qty'] ?? 0), 3, ',', '.')) ?></td>
                                        <td><?= e(number_format((float)($r['avg_purchase_price'] ?? 0), 2, ',', '.')) ?></td>
                                        <td><?= e(number_format((float)($r['total_value_raw'] ?? 0), 2, ',', '.')) ?></td>
                                        <td><?= e((string)((int)($r['barcode_count'] ?? 0))) ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                    <?php if ($stockTypeUi === 'Product'): ?>
                        <div class="panel" style="margin:12px 0">
                            <h3><?= e(ui_text('perm_search', 'Barkod Sorgulama', 'Barcode Search')) ?></h3>
                            <form method="get" class="row">
                                <input type="hidden" name="cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="sub" value="<?= e($sub) ?>">
                                <input type="text" name="barcode" value="<?= e($barcodeSearchUi) ?>" placeholder="<?= e(ui_text('web_barcode_only_ph', 'Sadece barkod girin...', 'Enter barcode only...')) ?>" style="max-width:420px">
                                <button class="btnp" type="submit"><?= e(ui_text('search', 'Sorgula', 'Search')) ?></button>
                            </form>
                        </div>
                        <div class="tbl">
                            <table>
                                <thead><tr><th><?= e(ui_text('barcode', 'Barkod', 'Barcode')) ?></th><th><?= e(ui_text('type', 'Tip', 'Type')) ?></th><th><?= e(ui_text('product', 'Ürün', 'Product')) ?></th><th><?= e(ui_text('web_stock_depots', 'Depo', 'Depot')) ?></th><th><?= e(ui_text('quantity_amount', 'Miktar', 'Quantity')) ?></th><th><?= e(ui_text('gram', 'Gram', 'Gram')) ?></th></tr></thead>
                                <tbody>
                                <?php if (!$stockBarcodeRows): ?>
                                    <tr><td colspan="6" class="muted"><?= e(ui_text('no_results', 'Sonuç yok.', 'No results.')) ?></td></tr>
                                <?php else: ?>
                                    <?php foreach ($stockBarcodeRows as $r): ?>
                                        <tr>
                                            <td><?= e((string)($r['unique_barcode'] ?? '')) ?></td>
                                            <td><?= e(stockTypeLabel((string)($r['stock_type'] ?? 'Product'))) ?></td>
                                            <td><?= e((string)($r['item_type_name'] ?? '')) ?></td>
                                            <td><?= e((string)($r['depot_name'] ?? 'Depo')) ?></td>
                                            <td><?= e(number_format((float)($r['quantity'] ?? 0), 3, ',', '.')) ?></td>
                                            <td><?= e(number_format((float)($r['quantity_gr'] ?? 0), 3, ',', '.')) ?></td>
                                        </tr>
                                    <?php endforeach; ?>
                                <?php endif; ?>
                                </tbody>
                            </table>
                        </div>
                    <?php endif; ?>

                <?php elseif ($sub === 'stock-categories'): ?>
                    <div class="g2">
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('add_category', 'Kategori Ekle', 'Add Category')) ?></h3>
                            <form method="post" class="row">
                                <input type="hidden" name="action" value="add_category">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <input type="text" name="category_name" required placeholder="<?= e(ui_text('web_category_name_ph', 'Kategori adı...', 'Category name...')) ?>" style="max-width:320px">
                                <button class="btnp" type="submit"><?= e(ui_text('add', 'Ekle', 'Add')) ?></button>
                            </form>
                        </div>
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('info', 'Not', 'Note')) ?></h3>
                            <p class="muted"><?= e(ui_text('web_category_note', 'Kategori silme işlemi tanımlara otomatik etki etmez, sadece kategori listesi güncellenir.', 'Deleting a category does not automatically affect definitions; it only updates the category list.')) ?></p>
                        </div>
                    </div>
                    <div class="tbl" style="margin-top:12px">
                        <table>
                            <thead><tr><th><?= e(ui_text('categories', 'Kategori', 'Category')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$stockCategoryRows): ?>
                                <tr><td colspan="2" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($stockCategoryRows as $r): ?>
                                    <tr>
                                        <td><?= e((string)($r['name'] ?? '')) ?></td>
                                        <td>
                                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_category', 'Kategori silinsin mi?', 'Delete category?')) ?>');">
                                                <input type="hidden" name="action" value="delete_category">
                                                <input type="hidden" name="category_id" value="<?= e((string)($r['id'] ?? '')) ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button type="submit" class="btnd"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'customer-persons'): ?>
                    <?php
                    $editingPersonId = trim((string)($_GET['edit_person_id'] ?? ''));
                    $editingPerson = null;
                    if ($editingPersonId !== '') {
                        foreach ($persons as $tmpPerson) {
                            if ((string)($tmpPerson['id'] ?? '') === $editingPersonId) {
                                $editingPerson = $tmpPerson;
                                break;
                            }
                        }
                        if (!$editingPerson) {
                            $editingPersonId = '';
                        }
                    }
                    ?>
                    <div class="g2">
                        <div>
                            <h3><?= e($editingPerson ? ui_text('edit_customer', 'Müşteri Düzenle', 'Edit Customer') : ui_text('add_customer', 'Müşteri Ekle', 'Add Customer')) ?></h3>
                            <form method="post" class="fg">
                                <input type="hidden" name="action" value="<?= ($editingPerson ? 'update_person' : 'add_person') ?>">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <?php if ($editingPerson): ?>
                                    <input type="hidden" name="person_id" value="<?= e($editingPerson['id'] ?? '') ?>">
                                <?php endif; ?>
                                <div class="f"><label><?= e(ui_text('full_name', 'Ad Soyad', 'Full Name')) ?></label><input type="text" name="full_name" value="<?= e($editingPerson['full_name'] ?? '') ?>" required></div>
                                <div class="f"><label><?= e(ui_text('phone', 'Telefon', 'Phone')) ?></label><input type="text" name="phone" value="<?= e($editingPerson['phone_number'] ?? '') ?>"></div>
                                <div class="f" style="grid-column:span 2"><label><?= e(ui_text('address', 'Adres', 'Address')) ?></label><input type="text" name="address" value="<?= e($editingPerson['address'] ?? '') ?>"></div>
                                <div class="f" style="grid-column:span 2"><label><?= e(ui_text('notes', 'Not', 'Notes')) ?></label><textarea name="notes"><?= e($editingPerson['notes'] ?? '') ?></textarea></div>
                                <div class="f">
                                    <label>&nbsp;</label>
                                    <button class="btnp" type="submit"><?= e($editingPerson ? ui_text('update', 'Güncelle', 'Update') : ui_text('save', 'Kaydet', 'Save')) ?></button>
                                </div>
                                <?php if ($editingPerson): ?>
                                    <div class="f">
                                        <label>&nbsp;</label>
                                        <a class="btn" href="<?= e(toUrl(['cat' => $cat, 'sub' => $sub])) ?>"><?= e(ui_text('cancel', 'İptal', 'Cancel')) ?></a>
                                    </div>
                                <?php endif; ?>
                            </form>
                        </div>
                        <div>
                            <h3><?= e(ui_text('customer_list', 'Müşteri Listesi', 'Customer List')) ?></h3>
                            <div class="tbl">
                                <table>
                                    <thead><tr><th><?= e(ui_text('full_name', 'Ad Soyad', 'Full Name')) ?></th><th><?= e(ui_text('phone', 'Telefon', 'Phone')) ?></th><th><?= e(ui_text('address', 'Adres', 'Address')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                                    <tbody>
                                    <?php if (!$persons): ?>
                                        <tr><td colspan="4" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                                    <?php else: ?>
                                        <?php foreach ($persons as $p): ?>
                                            <tr>
                                                <td><?= e($p['full_name'] ?? '') ?></td>
                                                <td><?= e($p['phone_number'] ?? '') ?></td>
                                                <td><?= e($p['address'] ?? '') ?></td>
                                                <td>
                                                    <a class="btn" href="<?= e(toUrl(['cat' => $cat, 'sub' => $sub, 'edit_person_id' => (string)($p['id'] ?? '')])) ?>"><?= e(ui_text('edit', 'Düzenle', 'Edit')) ?></a>
                                                    <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_customer', 'Müşteri silinsin mi?', 'Delete customer?')) ?>');">
                                                        <input type="hidden" name="action" value="delete_person">
                                                        <input type="hidden" name="person_id" value="<?= e($p['id'] ?? '') ?>">
                                                        <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                        <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                        <button type="submit" class="btnd"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                                    </form>
                                                </td>
                                            </tr>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </div>

                <?php elseif ($isCustomerAccountsSub): ?>
                    <?php
                    $allAssetTypes = [];
                    $assetKindMap = [];
                    foreach ($SUPPORTED_CURRENCIES as $assetName) {
                        $assetName = trim((string)$assetName);
                        if ($assetName !== '') {
                            $allAssetTypes[] = $assetName;
                            $assetKindMap[$assetName] = 'currency';
                        }
                    }
                    foreach ($productNames as $assetName) {
                        $assetName = trim((string)$assetName);
                        if ($assetName !== '' && !in_array($assetName, $allAssetTypes, true)) {
                            $allAssetTypes[] = $assetName;
                            $assetKindMap[$assetName] = 'product';
                        }
                    }
                    foreach ($ziynetNames as $assetName) {
                        $assetName = trim((string)$assetName);
                        if ($assetName !== '' && !in_array($assetName, $allAssetTypes, true)) {
                            $allAssetTypes[] = $assetName;
                            $assetKindMap[$assetName] = 'ziynet';
                        }
                    }
                    foreach ($scrapNames as $assetName) {
                        $assetName = trim((string)$assetName);
                        if ($assetName !== '' && !in_array($assetName, $allAssetTypes, true)) {
                            $allAssetTypes[] = $assetName;
                            $assetKindMap[$assetName] = 'scrap';
                        }
                    }
                    $customerRouteBase = ['cat' => $cat, 'sub' => $sub];
                    if ($selectedPerson !== '') {
                        $customerRouteBase['person'] = $selectedPerson;
                    }
                    $personNameList = [];
                    foreach ($persons as $p) {
                        $pn = trim((string)($p['full_name'] ?? ''));
                        if ($pn !== '') {
                            $personNameList[] = $pn;
                        }
                    }
                    ?>
                    <h3><?= e(ui_text('accounts_tab', 'Cari Hesaplar', 'Accounts')) ?></h3>
                    <form method="get" class="row customer-picker-form" style="margin-bottom:10px">
                        <input type="hidden" name="cat" value="<?= e($cat) ?>">
                        <input type="hidden" name="sub" value="<?= e($sub) ?>">
                        <?php if ($selectedOp !== ''): ?>
                            <input type="hidden" name="op" value="<?= e($selectedOp) ?>">
                        <?php endif; ?>
                        <div class="autocomplete" data-autocomplete data-options='<?= e(json_encode($personNameList, JSON_UNESCAPED_UNICODE)) ?>' data-submit-form="true">
                            <input type="text" data-autocomplete-input value="<?= e($selectedPerson) ?>" placeholder="<?= e(ui_text('customer_name_ph', 'Müşteri adı yazın...', 'Type customer name...')) ?>" autocomplete="off" required>
                            <input type="hidden" name="person" data-autocomplete-hidden value="<?= e($selectedPerson) ?>" id="nav_person_hidden" required>
                            <div class="autocomplete-list" data-autocomplete-list></div>
                        </div>
                        <button type="button" class="btn" onclick="navCustomer(-1)" style="padding: 10px; min-width: 40px;">◀</button>
                        <button type="submit" class="btnp" style="padding: 10px 20px;"><?= e(ui_text('select', 'Seç', 'Select')) ?></button>
                        <button type="button" class="btn" onclick="navCustomer(1)" style="padding: 10px; min-width: 40px;">▶</button>
                    </form>

                    <script>
                    function navCustomer(direction) {
                        const customers = <?= json_encode($personNameList, JSON_UNESCAPED_UNICODE) ?>;
                        const current = document.getElementById('nav_person_hidden').value;
                        if (customers.length === 0) return;

                        let idx = customers.indexOf(current);
                        if (idx === -1) idx = 0;
                        else idx += direction;

                        if (idx < 0) idx = customers.length - 1;
                        if (idx >= customers.length) idx = 0;

                        document.getElementById('nav_person_hidden').value = customers[idx];
                        document.querySelector('.customer-picker-form').submit();
                    }
                    </script>

                    <?php if ($selectedPerson === ''): ?>
                        <div class="panel" style="margin:0">
                            <p class="muted"><?= e(ui_text('customer_required_hint', 'İşlem ekranını açmak için önce müşteri seçimi zorunludur.', 'Select a customer first to open the transaction screen.')) ?></p>
                        </div>
                    <?php else: ?>
                        <div class="panel" style="margin:0 0 12px">
                            <h3><?= e($selectedPerson) ?> - <?= e(ui_text('current_balance', 'Güncel Bakiye', 'Current Balance')) ?></h3>
                            <?php
                            $borcluRows = [];
                            $alacakliRows = [];
                            foreach ($personBalanceRows as $b) {
                                $balType = (string)($b['currency'] ?? '');
                                $balVal = (float)($b['net_balance'] ?? 0);
                                if ($balVal > 0) {
                                    $borcluRows[] = ['type' => $balType, 'val' => $balVal];
                                } elseif ($balVal < 0) {
                                    $alacakliRows[] = ['type' => $balType, 'val' => abs($balVal)];
                                }
                            }
                            ?>
                            <div class="balance-grid">
                                <div class="balance-box debt-box">
                                    <h4><?= e(ui_text('debt', 'Borçlu', 'Debt')) ?></h4>
                                    <?php if (!$borcluRows): ?>
                                        <span class="muted"><?= e(ui_text('none', 'Yok', 'None')) ?></span>
                                    <?php else: ?>
                                        <?php foreach ($borcluRows as $b): ?>
                                            <span class="balance-chip neg"><?= e($b['type']) ?>: <?= e(formatAmountByType((float)$b['val'], (string)$b['type'], $SUPPORTED_CURRENCIES)) ?></span>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </div>
                                <div class="balance-box credit-box">
                                    <h4><?= e(ui_text('credit', 'Alacaklı', 'Credit')) ?></h4>
                                    <?php if (!$alacakliRows): ?>
                                        <span class="muted"><?= e(ui_text('none', 'Yok', 'None')) ?></span>
                                    <?php else: ?>
                                        <?php foreach ($alacakliRows as $b): ?>
                                            <span class="balance-chip pos"><?= e($b['type']) ?>: <?= e(formatAmountByType((float)$b['val'], (string)$b['type'], $SUPPORTED_CURRENCIES)) ?></span>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </div>
                            </div>
                        </div>

                        <div class="tabs op-tabs">
                            <?php
                            $ops = [
                                'cash' => ui_text('cash_tab', 'Nakit', 'Cash'),
                                'gold' => ui_text('product_status_tab', 'Altın', 'Gold'),
                                'ziynet' => 'Ziynet',
                                'scrap' => ui_text('scrap_status_tab', 'Hurda', 'Scrap'),
                                'exchange' => ui_text('web_exchange', 'Çeviri', 'Exchange'),
                                'virman' => ui_text('web_virman', 'Virman', 'Transfer'),
                            ];
                            foreach ($ops as $opKey => $opLabel):
                                $u = array_merge($customerRouteBase, ['op' => $opKey]);
                            ?>
                                <a class="<?= ($selectedOp === $opKey ? 'active' : '') ?>" href="<?= e(toUrl($u)) ?>"><?= e($opLabel) ?></a>
                            <?php endforeach; ?>
                        </div>

                        <div class="panel" style="margin:0 0 12px">
                            <?php if ($selectedOp === 'cash'): ?>
                                <?php
                                $cashDirectionDefault = trim((string)($_GET['cash_direction'] ?? 'CashIn'));
                                if (!in_array($cashDirectionDefault, ['CashIn', 'CashOut'], true)) {
                                    $cashDirectionDefault = 'CashIn';
                                }
                                ?>
                                <h3><?= e(ui_text('cash_transaction', 'Nakit İşlemi', 'Cash Transaction')) ?></h3>
                                <form method="post" class="compact-form" id="customer-cash-form">
                                    <input type="hidden" name="action" value="customer_cash_txn">
                                    <input type="hidden" name="person_name" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="return_person" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_op" value="<?= e($selectedOp) ?>">
                                    <input type="hidden" name="cash_direction" id="cash_direction_input" value="<?= e($cashDirectionDefault) ?>">
                                    <div class="seg-tabs" data-seg-tabs data-target-input="cash_direction_input">
                                        <button type="button" data-value="CashIn" class="<?= ($cashDirectionDefault === 'CashIn' ? 'active' : '') ?>"><?= e(ui_text('cash_in', 'Nakit Girişi', 'Cash In')) ?></button>
                                        <button type="button" data-value="CashOut" class="<?= ($cashDirectionDefault === 'CashOut' ? 'active' : '') ?>"><?= e(ui_text('cash_out', 'Nakit Çıkışı', 'Cash Out')) ?></button>
                                    </div>
                                    <div class="fg compact-grid">
                                        <div class="f">
                                            <label><?= e(ui_text('payment_type', 'Ödeme Türü', 'Payment Type')) ?></label>
                                            <select name="payment_type" id="cash_hoca_type" required>
                                                <option value="cash"><?= e(ui_text('cash_tab', 'Nakit', 'Cash')) ?></option>
                                                <option value="card"><?= e(ui_text('card', 'Kart', 'Card')) ?></option>
                                                <option value="transfer"><?= e(ui_text('bank_transfer', 'Havale', 'Transfer')) ?></option>
                                            </select>
                                        </div>
                                        <div class="f">
                                            <label><?= e(ui_text('cash_or_card', 'Kasa / Kart', 'Cash / Card')) ?></label>
                                            <select name="source_account_key" id="cash_account_select" required>
                                                <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                <?php foreach ($accounts as $a): ?>
                                                    <option data-atype="<?= e($a['type']) ?>" value="<?= e($a['type'] . '|' . $a['id']) ?>"><?= e((($a['type'] === 'cash') ? ui_text('cash_account_label', 'Kasa', 'Cash') : ui_text('card', 'Kart', 'Card')) . ' - ' . $a['name']) ?></option>
                                                <?php endforeach; ?>
                                            </select>
                                        </div>
                                        <div class="f"><label><?= e(ui_text('amount', 'Tutar', 'Amount')) ?></label><input type="text" name="amount" placeholder="0,00" required></div>
                                        <div class="f"><label><?= e(ui_text('currency', 'Para Birimi', 'Currency')) ?></label><select name="currency" required><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>"><?= e($c) ?></option><?php endforeach; ?></select></div>
                                        <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="transaction_date" value="<?= e(date('Y-m-d')) ?>"></div>
                                        <div class="f span-2"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description" placeholder="<?= e(ui_text('description', 'Açıklama', 'Description')) ?>"></div>
                                        <div class="f"><label><?= e(ui_text('affect_cash', 'Kasayı Etkile', 'Affect Cash')) ?></label><label style="margin-top: 5px;"><input type="checkbox" name="affect_cash" value="1" checked> <?= e(ui_text('reflect_to_cash', 'İşlem kasaya yansısın', 'Reflect this transaction to cash')) ?></label></div>
                                        <div class="f"><label>&nbsp;</label><button class="btnp" type="submit" style="height: 40px;"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                                    </div>
                                </form>
                            <?php elseif (in_array($selectedOp, ['gold', 'ziynet', 'scrap'], true)): ?>
                                <?php
                                $stockType = 'Product';
                                $stockLabel = 'Altın';
                                $stockNameSource = $productNames;
                                $stockMetaSource = $productMeta;
                                if ($selectedOp === 'ziynet') {
                                    $stockType = 'Ziynet';
                                    $stockLabel = 'Ziynet';
                                    $stockNameSource = $ziynetNames;
                                    $stockMetaSource = $ziynetMeta;
                                } elseif ($selectedOp === 'scrap') {
                                    $stockType = 'Scrap';
                                    $stockLabel = 'Hurda';
                                    $stockNameSource = $scrapNames;
                                    $stockMetaSource = $scrapMeta;
                                }
                                $stockModeDefault = trim((string)($_GET['stock_mode'] ?? 'Purchase'));
                                if (!in_array($stockModeDefault, ['Purchase', 'Sale', 'PaidIn', 'PaidOut'], true)) {
                                    $stockModeDefault = 'Purchase';
                                }
                                ?>
                                <h3><?= e($stockLabel) ?> <?= e(ui_text('transaction_suffix', 'İşlemi', 'Transaction')) ?></h3>
                                <form method="post" class="compact-form" id="customer-stock-form"
                                      data-stock-meta='<?= e(json_encode($stockMetaSource, JSON_UNESCAPED_UNICODE)) ?>'
                                      data-barcode-meta='<?= e(json_encode($barcodeMeta, JSON_UNESCAPED_UNICODE)) ?>'
                                      data-rates='<?= e(json_encode($dashboardRates, JSON_UNESCAPED_UNICODE)) ?>'>
                                    <input type="hidden" name="action" value="customer_stock_txn">
                                    <input type="hidden" name="person_name" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" id="stock_type_input" name="stock_type" value="<?= e($stockType) ?>">
                                    <input type="hidden" id="stock_mode_select" name="stock_mode" value="<?= e($stockModeDefault) ?>">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="return_person" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_op" value="<?= e($selectedOp) ?>">
                                    <div class="seg-tabs" data-seg-tabs data-target-input="stock_mode_select">
                                        <button type="button" data-value="Purchase" class="<?= ($stockModeDefault === 'Purchase' ? 'active' : '') ?>"><?= e(ui_text('buy_price', 'Alış', 'Buy')) ?></button>
                                        <button type="button" data-value="Sale" class="<?= ($stockModeDefault === 'Sale' ? 'active' : '') ?>"><?= e(ui_text('sell_price', 'Satış', 'Sell')) ?></button>
                                        <button type="button" data-value="PaidIn" class="<?= ($stockModeDefault === 'PaidIn' ? 'active' : '') ?>"><?= e(ui_text('paid_in', 'Bedelli Giriş', 'Paid In')) ?></button>
                                        <button type="button" data-value="PaidOut" class="<?= ($stockModeDefault === 'PaidOut' ? 'active' : '') ?>"><?= e(ui_text('paid_out', 'Bedelli Çıkış', 'Paid Out')) ?></button>
                                    </div>
                                    <div class="fg compact-grid">
                                        <div class="f">
                                            <label><?= e(ui_text('product', 'Ürün', 'Product')) ?></label>
                                            <select name="item_name" id="stock_item_select" required>
                                                <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                <?php foreach ($stockNameSource as $sn): ?>
                                                    <?php $sm = $stockMetaSource[$sn] ?? []; ?>
                                                    <option
                                                        value="<?= e($sn) ?>"
                                                        data-buy-price="<?= e((string)($sm['buy_price'] ?? 0)) ?>"
                                                        data-sell-price="<?= e((string)($sm['sell_price'] ?? 0)) ?>"
                                                        data-buy-currency="<?= e((string)($sm['buy_currency'] ?? 'TRY')) ?>"
                                                        data-sell-currency="<?= e((string)($sm['sell_currency'] ?? 'TRY')) ?>"
                                                    ><?= e($sn) ?></option>
                                                <?php endforeach; ?>
                                            </select>
                                        </div>
                                        <div class="f"><label><?= e(ui_text('quantity', 'Miktar', 'Quantity')) ?></label><input type="text" id="stock_qty_input" name="quantity" placeholder="0,000" required></div>
                                        <div class="f">
                                            <label><?= e(ui_text('depot', 'Depo', 'Depot')) ?></label>
                                            <select name="depot_name" required>
                                                <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                <?php foreach ($depotNames as $dn): ?>
                                                    <option value="<?= e($dn) ?>"><?= e($dn) ?></option>
                                                <?php endforeach; ?>
                                            </select>
                                        </div>
                                        <div class="f"><label><?= e(ui_text('unit_price', 'Birim Fiyat', 'Unit Price')) ?></label><input type="text" id="stock_unit_price" name="unit_price" placeholder="0,00"></div>
                                        <div class="f"><label><?= e(ui_text('base_currency', 'Ana Para Birimi', 'Base Currency')) ?></label><select name="currency" id="stock_currency_select" required><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>"><?= e($c) ?></option><?php endforeach; ?></select></div>
                                    </div>

                                    <div id="stock_gold_fields" class="fg<?= ($stockType === 'Product' ? '' : ' hidden') ?> stock-box">
                                        <div class="f span-4">
                                            <label><?= e(ui_text('barcode_rows', 'Barkod Satırları', 'Barcode Rows')) ?></label>
                                            <div id="stock_barcode_rows"></div>
                                        </div>
                                    </div>
                                    <div id="stock_paid_fields" class="fg stock-box<?= in_array($stockModeDefault, ['Purchase', 'Sale', 'PaidIn', 'PaidOut'], true) ? '' : ' hidden' ?>">
                                        <div class="f span-4">
                                            <div class="row" style="justify-content:space-between">
                                                <strong id="stock_payment_title"><?= e(ui_text('payments', 'Ödemeler', 'Payments')) ?></strong>
                                                <button type="button" id="bedel_add_btn" class="btn"><?= e(ui_text('add_payment', 'Ödeme Ekle', 'Add Payment')) ?></button>
                                            </div>
                                            <div id="stock_payment_summary" class="fg compact-grid" style="margin:8px 0">
                                                <div class="f">
                                                    <label><?= e(ui_text('main_amount', 'Ana Tutar', 'Main Amount')) ?></label>
                                                    <div class="tag" id="stock_main_total_text">0,00</div>
                                                </div>
                                                <div class="f">
                                                    <label><?= e(ui_text('payment_total', 'Ödeme Toplamı', 'Payment Total')) ?></label>
                                                    <div class="tag" id="stock_paid_total_text">0,00</div>
                                                </div>
                                                <div class="f">
                                                    <label><?= e(ui_text('difference', 'Fark', 'Difference')) ?></label>
                                                    <div class="tag" id="stock_paid_diff_text">0,00</div>
                                                </div>
                                                <div class="f"><label><?= e(ui_text('stock_effect', 'Stok Etkisi', 'Stock Effect')) ?></label><label><input type="checkbox" name="affect_stock" checked> <?= e(ui_text('yes', 'Evet', 'Yes')) ?></label></div>
                                            </div>
                                            <div class="panel" id="stock_tax_block" style="margin:8px 0">
                                                <div class="row" style="justify-content:space-between">
                                                    <label><input type="checkbox" name="apply_taxes" id="stock_apply_taxes"> <?= e(ui_text('apply_tax', 'Vergi Uygula', 'Apply Tax')) ?></label>
                                                    <span class="muted"><?= e(ui_text('tax_snapshot_note', 'Alış ve satış akışları aynı vergi anlık görüntü mantığını kullanır', 'Purchase and sale flows use the same tax snapshot logic')) ?></span>
                                                </div>
                                                <div id="stock_tax_list" class="fg compact-grid hidden" style="margin-top:8px">
                                                    <?php if (!$activeTaxRows): ?>
                                                        <div class="f"><span class="muted"><?= e(ui_text('no_active_tax_definition', 'Aktif vergi tanımı yok.', 'There is no active tax definition.')) ?></span></div>
                                                    <?php else: ?>
                                                        <?php foreach ($activeTaxRows as $tx): ?>
                                                            <?php
                                                            $txId = trim((string)($tx['id'] ?? ''));
                                                            $txName = trim((string)($tx['name'] ?? ''));
                                                            $txType = strtolower(trim((string)($tx['type'] ?? 'percent')));
                                                            $txRate = (float)($tx['rate'] ?? 0);
                                                            if ($txId === '' || $txName === '') { continue; }
                                                            ?>
                                                            <div class="f">
                                                                <label>
                                                                    <input type="checkbox"
                                                                           class="stock-tax-item"
                                                                           name="selected_tax_ids[]"
                                                                           value="<?= e($txId) ?>"
                                                                           data-tax-type="<?= e($txType) ?>"
                                                                           data-tax-rate="<?= e(number_format($txRate, 4, '.', '')) ?>">
                                                                    <?= e($txName) ?>
                                                                    <span class="muted">(<?= e($txType === 'fixed' ? ui_text('fixed', 'Sabit', 'Fixed') : '%') ?> <?= e(number_format($txRate, 2, ',', '.')) ?>)</span>
                                                                </label>
                                                            </div>
                                                        <?php endforeach; ?>
                                                    <?php endif; ?>
                                                </div>
                                            </div>
                                            <div id="bedel_rows"></div>
                                            <template id="bedel_row_tpl">
                                                <div class="row bedel-row payment-row">
                                                    <input type="hidden" name="payment_kind_list[]" class="payment-kind" value="">
                                                    <div class="f" style="min-width:180px;flex:1">
                                                        <label><?= e(ui_text('asset_type', 'Karşılık Türü', 'Asset Type')) ?></label>
                                                        <select name="payment_type_list[]" class="bedel-type payment-type" required>
                                                            <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                            <optgroup label="<?= e(ui_text('currencies', 'Para Birimleri', 'Currencies')) ?>">
                                                                <?php foreach ($SUPPORTED_CURRENCIES as $c): ?>
                                                                    <option data-kind="currency" value="<?= e($c) ?>"><?= e($c) ?></option>
                                                                <?php endforeach; ?>
                                                            </optgroup>
                                                            <optgroup label="<?= e(ui_text('gold_product_group', 'Altın/Ürün', 'Gold/Product')) ?>">
                                                                <?php foreach ($allAssetTypes as $at): if(!in_array($at, $SUPPORTED_CURRENCIES)) { ?>
                                                                    <option data-kind="<?= e($assetKindMap[$at] ?? 'product') ?>" value="<?= e($at) ?>"><?= e($at) ?></option>
                                                                <?php } endforeach; ?>
                                                            </optgroup>
                                                        </select>
                                                    </div>
                                                    <div class="f payment-qty-wrap" style="min-width:110px">
                                                        <label><?= e(ui_text('quantity', 'Miktar', 'Quantity')) ?></label>
                                                        <input type="text" name="payment_qty_list[]" class="bedel-amount payment-qty" placeholder="0,00">
                                                    </div>
                                                    <div class="f payment-unit-wrap hidden" style="min-width:120px">
                                                        <label><?= e(ui_text('unit_price', 'Birim Fiyat', 'Unit Price')) ?></label>
                                                        <input type="text" name="payment_unit_price_list[]" class="payment-unit-price" placeholder="0,00">
                                                    </div>
                                                    <div class="f payment-currency-wrap" style="min-width:95px">
                                                        <label><?= e(ui_text('currency_abbr', 'PB', 'Cur')) ?></label>
                                                        <select name="payment_currency_list[]" class="payment-currency">
                                                            <?php foreach ($SUPPORTED_CURRENCIES as $c): ?>
                                                                <option value="<?= e($c) ?>"><?= e($c) ?></option>
                                                            <?php endforeach; ?>
                                                        </select>
                                                    </div>
                                                    <div class="f payment-account-wrap hidden" style="min-width:180px;flex:1">
                                                        <label><?= e(ui_text('cash_or_card', 'Kasa / Kart', 'Cash / Card')) ?></label>
                                                        <select name="payment_account_key_list[]" class="payment-account">
                                                            <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                            <?php foreach ($accounts as $a): ?>
                                                                <option data-atype="<?= e($a['type']) ?>" value="<?= e($a['type'] . '|' . $a['id']) ?>"><?= e((($a['type'] === 'cash') ? ui_text('cash_account_label', 'Kasa', 'Cash') : ui_text('card', 'Kart', 'Card')) . ' - ' . $a['name']) ?></option>
                                                            <?php endforeach; ?>
                                                        </select>
                                                    </div>
                                                    <div class="f payment-depot-wrap hidden" style="min-width:150px">
                                                        <label><?= e(ui_text('depot', 'Depo', 'Depot')) ?></label>
                                                        <select name="payment_depot_list[]" class="payment-depot">
                                                            <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                            <?php foreach ($depotNames as $dn): ?>
                                                                <option value="<?= e($dn) ?>"><?= e($dn) ?></option>
                                                            <?php endforeach; ?>
                                                        </select>
                                                    </div>
                                                    <div class="f bedel-gold-field hidden" style="min-width:100px">
                                                        <label><?= e(ui_text('gram', 'Gram', 'Gram')) ?></label>
                                                        <input type="text" name="payment_gram_list[]" class="bedel-gram payment-gram" placeholder="0,000">
                                                    </div>
                                                    <div class="f bedel-gold-field hidden" style="min-width:90px">
                                                        <label><?= e(ui_text('karat', 'Ayar', 'Karat')) ?></label>
                                                        <input type="text" name="payment_karat_list[]" class="bedel-karat payment-karat" placeholder="22K">
                                                    </div>
                                                    <div class="f payment-line-total-wrap" style="min-width:100px">
                                                        <label><?= e(ui_text('amount', 'Tutar', 'Amount')) ?></label>
                                                        <input type="text" class="payment-line-total" value="0,00" readonly>
                                                    </div>
                                                    <button type="button" class="btnd bedel-remove"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                                </div>
                                            </template>
                                        </div>
                                    </div>

                                    <div class="fg compact-grid">
                                        <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="transaction_date" value="<?= e(date('Y-m-d')) ?>"></div>
                                        <div class="f span-2"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description"></div>
                                        <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                                    </div>
                                </form>
                            <?php elseif ($selectedOp === 'exchange'): ?>
                                <?php
                                $exchangeModeDefault = trim((string)($_GET['exchange_mode'] ?? 'ExchangePayable'));
                                if (!in_array($exchangeModeDefault, ['ExchangePayable', 'ExchangeReceivable'], true)) {
                                    $exchangeModeDefault = 'ExchangePayable';
                                }
                                ?>
                                <h3><?= e(ui_text('web_exchange_transaction', 'Çeviri İşlemi', 'Exchange Transaction')) ?></h3>
                                <form method="post" class="compact-form" id="customer-exchange-form">
                                    <input type="hidden" name="action" value="customer_exchange">
                                    <input type="hidden" name="person_name" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="return_person" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_op" value="<?= e($selectedOp) ?>">
                                    <input type="hidden" name="out_kind" id="exchange_out_kind" value="currency">
                                    <input type="hidden" name="in_kind" id="exchange_in_kind" value="currency">
                                    <input type="hidden" name="exchange_mode" id="exchange_mode_input" value="<?= e($exchangeModeDefault) ?>">
                                    <div class="seg-tabs" data-seg-tabs data-target-input="exchange_mode_input">
                                        <button type="button" data-value="ExchangePayable" class="<?= ($exchangeModeDefault === 'ExchangePayable' ? 'active' : '') ?>"><?= e(ui_text('exchange_payable', 'Çeviri Borç', 'Exchange Payable')) ?></button>
                                        <button type="button" data-value="ExchangeReceivable" class="<?= ($exchangeModeDefault === 'ExchangeReceivable' ? 'active' : '') ?>"><?= e(ui_text('exchange_receivable', 'Çeviri Alacak', 'Exchange Receivable')) ?></button>
                                    </div>
                                    <div class="exchange-grid">
                                        <div class="exchange-side exchange-out">
                                            <h4><?= e(ui_text('given', 'Verilen', 'Given')) ?></h4>
                                            <div class="f">
                                                <label><?= e(ui_text('type', 'Tür', 'Type')) ?></label>
                                                <select name="out_type" id="exchange_out_type" required>
                                                    <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                    <?php foreach ($allAssetTypes as $at): ?>
                                                        <option data-kind="<?= e($assetKindMap[$at] ?? 'currency') ?>" value="<?= e($at) ?>"><?= e($at) ?></option>
                                                    <?php endforeach; ?>
                                                </select>
                                            </div>
                                            <div class="f"><label id="exchange_out_amount_label"><?= e(ui_text('quantity', 'Miktar', 'Quantity')) ?></label><input type="text" name="out_amount" id="exchange_out_amount" placeholder="0,00" required></div>
                                            <div class="f hidden" id="exchange_out_gram_wrap"><label><?= e(ui_text('gram', 'Gram', 'Gram')) ?></label><input type="text" name="out_gram" id="exchange_out_gram" placeholder="0,000"></div>
                                            <div class="f hidden" id="exchange_out_karat_wrap"><label><?= e(ui_text('karat', 'Ayar', 'Karat')) ?></label><input type="text" name="out_karat" id="exchange_out_karat" placeholder="22K"></div>
                                        </div>
                                        <div class="exchange-side exchange-in">
                                            <h4><?= e(ui_text('received', 'Alınan', 'Received')) ?></h4>
                                            <div class="f">
                                                <label><?= e(ui_text('type', 'Tür', 'Type')) ?></label>
                                                <select name="in_type" id="exchange_in_type" required>
                                                    <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                    <?php foreach ($allAssetTypes as $at): ?>
                                                        <option data-kind="<?= e($assetKindMap[$at] ?? 'currency') ?>" value="<?= e($at) ?>"><?= e($at) ?></option>
                                                    <?php endforeach; ?>
                                                </select>
                                            </div>
                                            <div class="f"><label id="exchange_in_amount_label"><?= e(ui_text('quantity', 'Miktar', 'Quantity')) ?></label><input type="text" name="in_amount" id="exchange_in_amount" placeholder="0,00" required></div>
                                            <div class="f hidden" id="exchange_in_gram_wrap"><label><?= e(ui_text('gram', 'Gram', 'Gram')) ?></label><input type="text" name="in_gram" id="exchange_in_gram" placeholder="0,000"></div>
                                            <div class="f hidden" id="exchange_in_karat_wrap"><label><?= e(ui_text('karat', 'Ayar', 'Karat')) ?></label><input type="text" name="in_karat" id="exchange_in_karat" placeholder="22K"></div>
                                        </div>
                                    </div>
                                    <div class="fg compact-grid">
                                        <div class="f">
                                            <label><?= e(ui_text('depot', 'Depo', 'Depot')) ?></label>
                                            <select name="depot_name">
                                                <?php foreach ($depotNames as $dn): ?>
                                                    <option value="<?= e($dn) ?>"><?= e($dn) ?></option>
                                                <?php endforeach; ?>
                                                <?php if (!$depotNames): ?><option value="<?= e(ui_text('default_depot_name', 'Merkez', 'Central')) ?>"><?= e(ui_text('default_depot_name', 'Merkez', 'Central')) ?></option><?php endif; ?>
                                            </select>
                                        </div>
                                        <div class="f"><label><?= e(ui_text('stock_effect', 'Stok Etkisi', 'Stock Effect')) ?></label><label><input type="checkbox" name="affect_stock" checked> <?= e(ui_text('yes', 'Evet', 'Yes')) ?></label></div>
                                        <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="transaction_date" value="<?= e(date('Y-m-d')) ?>"></div>
                                        <div class="f span-4"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description"></div>
                                        <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                                    </div>
                                </form>
                            <?php elseif ($selectedOp === 'virman'): ?>
                                <?php
                                $virmanModeDefault = trim((string)($_GET['virman_mode'] ?? 'VirmanPayable'));
                                if (!in_array($virmanModeDefault, ['VirmanPayable', 'VirmanReceivable'], true)) {
                                    $virmanModeDefault = 'VirmanPayable';
                                }
                                $targetPersonNames = [];
                                foreach ($persons as $p) {
                                    $pn = trim((string)($p['full_name'] ?? ''));
                                    if ($pn !== '' && $pn !== $selectedPerson) {
                                        $targetPersonNames[] = $pn;
                                    }
                                }
                                ?>
                                <h3><?= e(ui_text('virman_transaction', 'Virman İşlemi', 'Transfer Transaction')) ?></h3>
                                <form method="post" class="compact-form" id="customer-virman-form">
                                    <input type="hidden" name="action" value="customer_virman">
                                    <input type="hidden" name="source_person" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="return_person" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_op" value="<?= e($selectedOp) ?>">
                                    <input type="hidden" name="virman_mode" id="virman_mode_input" value="<?= e($virmanModeDefault) ?>">
                                    <div class="seg-tabs" data-seg-tabs data-target-input="virman_mode_input">
                                        <button type="button" data-value="VirmanPayable" class="<?= ($virmanModeDefault === 'VirmanPayable' ? 'active' : '') ?>"><?= e(ui_text('virman_payable', 'Virman Borç', 'Transfer Payable')) ?></button>
                                        <button type="button" data-value="VirmanReceivable" class="<?= ($virmanModeDefault === 'VirmanReceivable' ? 'active' : '') ?>"><?= e(ui_text('virman_receivable', 'Virman Alacak', 'Transfer Receivable')) ?></button>
                                    </div>
                                    <div class="fg compact-grid">
                                        <div class="f span-2">
                                            <label><?= e(ui_text('target_customer', 'Hedef Müşteri', 'Target Customer')) ?></label>
                                            <div class="autocomplete" data-autocomplete data-options='<?= e(json_encode($targetPersonNames, JSON_UNESCAPED_UNICODE)) ?>'>
                                                <input type="text" data-autocomplete-input value="" placeholder="<?= e(ui_text('customer_name_ph', 'Müşteri adı yazın...', 'Type customer name...')) ?>" autocomplete="off" required>
                                                <input type="hidden" name="target_person" data-autocomplete-hidden value="" required>
                                                <div class="autocomplete-list" data-autocomplete-list></div>
                                            </div>
                                        </div>
                                        <div class="f">
                                            <label><?= e(ui_text('asset_type', 'Varlık Türü', 'Asset Type')) ?></label>
                                            <select name="asset_type" id="virman_asset_type" required>
                                                <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                                <?php foreach ($allAssetTypes as $at): ?>
                                                    <option data-kind="<?= e($assetKindMap[$at] ?? 'currency') ?>" value="<?= e($at) ?>"><?= e($at) ?></option>
                                                <?php endforeach; ?>
                                            </select>
                                        </div>
                                        <div class="f"><label><?= e(ui_text('quantity', 'Miktar', 'Quantity')) ?></label><input type="text" name="amount" placeholder="0,00" required></div>
                                        <div class="f hidden" id="virman_gram_wrap"><label><?= e(ui_text('gram', 'Gram', 'Gram')) ?></label><input type="text" name="asset_gram" id="virman_gram" placeholder="0,000"></div>
                                        <div class="f hidden" id="virman_karat_wrap"><label><?= e(ui_text('karat', 'Ayar', 'Karat')) ?></label><input type="text" name="asset_karat" id="virman_karat" placeholder="22K"></div>
                                        <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="transaction_date" value="<?= e(date('Y-m-d')) ?>"></div>
                                        <div class="f span-2"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description"></div>
                                        <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                                    </div>
                                </form>
                            <?php endif; ?>
                        </div>

                        <?php if ($historyEditRow): ?>
                            <?php
                            $editRawDesc = (string)($historyEditRow['description'] ?? '');
                            $editDisplayDesc = $editRawDesc;
                            if ($editRawDesc !== '' && substr($editRawDesc, 0, 1) === '{') {
                                $editJson = json_decode($editRawDesc, true);
                                if (is_array($editJson) && trim((string)($editJson['main_desc'] ?? '')) !== '') {
                                    $editDisplayDesc = (string)$editJson['main_desc'];
                                }
                            }
                            ?>
                            <div class="panel" style="margin:0 0 12px">
                                <h3><?= e(ui_text('edit_transaction', 'Hareket Düzenle', 'Edit Transaction')) ?></h3>
                                <form method="post" class="fg compact-grid">
                                    <input type="hidden" name="action" value="customer_event_update">
                                    <input type="hidden" name="event_id" value="<?= e($historyEditRow['id'] ?? '') ?>">
                                    <input type="hidden" name="person_name" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="return_person" value="<?= e($selectedPerson) ?>">
                                    <input type="hidden" name="return_op" value="<?= e($selectedOp) ?>">
                                    
                                    <!-- İşlem Tipi Değiştirilemez, Sadece Okunur -->
                                    <input type="hidden" name="event_type" value="<?= e($historyEditRow['event_type'] ?? '') ?>">
                                    <div class="f span-4" style="margin-bottom: 10px;">
                                        <span class="muted"><?= e(ui_text('transaction_type', 'İşlem Türü', 'Transaction Type')) ?>:</span>
                                        <strong><?= e($historyEditRow['event_type'] ?? '') ?></strong>
                                    </div>
                                    <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="transaction_date" value="<?= e(date('Y-m-d', strtotime((string)($historyEditRow['event_date'] ?? date('Y-m-d'))))) ?>"></div>
                                    <?php 
                                        $editAmount = ((float)($historyEditRow['debt_original'] ?? 0) > 0) ? (float)($historyEditRow['debt_original']) : (float)($historyEditRow['credit_original'] ?? 0);
                                    ?>
                                    <div class="f"><label><?= e(ui_text('amount', 'Tutar', 'Amount')) ?></label><input type="text" name="amount" value="<?= e(number_format($editAmount, 2, ',', '')) ?>" placeholder="0,00"></div>
                                    <div class="f"><label><?= e(ui_text('currency', 'Para Birimi', 'Currency')) ?></label><input type="text" name="currency" value="<?= e($historyEditRow['currency'] ?? '') ?>"></div>
                                    <div class="f" style="grid-column:span 2"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description" value="<?= e($editDisplayDesc) ?>"></div>
                                    <div class="f"><label>&nbsp;</label><button class="btnp" type="submit" style="height:40px;"><?= e(ui_text('update', 'Güncelle', 'Update')) ?></button></div>
                                </form>
                                <div class="help">
                                    <?= e(ui_text('json_edit_note', 'JSON kayıtlarda sadece ana açıklama güncellenir; miktar değişimi sade kayıtlar içindir.', 'Only the main description is updated for JSON records; amount changes apply to plain records.')) ?>
                                    <a href="<?= e(toUrl(array_merge($customerRouteBase, ['op' => $selectedOp]))) ?>"><?= e(ui_text('cancel', 'İptal', 'Cancel')) ?></a>
                                </div>
                            </div>
                        <?php endif; ?>

                        <h3 style="margin-top:12px"><?= e(ui_text('customer_ledger_table', 'Cari Hareket Tablosu', 'Customer Ledger Table')) ?></h3>
                        <div class="tbl">
                            <table>
                                <thead><tr><th><?= e(ui_text('date', 'Tarih', 'Date')) ?></th><th><?= e(ui_text('transaction', 'İşlem', 'Transaction')) ?></th><th><?= e(ui_text('amount', 'Tutar', 'Amount')) ?></th><th><?= e(ui_text('type', 'Tür', 'Type')) ?></th><th><?= e(ui_text('description', 'Açıklama', 'Description')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                                <tbody>
                                <?php if (!$historyRowsWithBalance): ?>
                                    <tr><td colspan="6" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                                <?php else: ?>
                                    <?php foreach ($historyRowsWithBalance as $h): ?>
                                        <?php
                                        $descRaw = (string)($h['description'] ?? '');
                                        $descDisplay = $descRaw;
                                        if ($descRaw !== '' && substr($descRaw, 0, 1) === '{') {
                                            $jsonDesc = json_decode($descRaw, true);
                                            if (is_array($jsonDesc)) {
                                                $descDisplay = trim((string)($jsonDesc['main_desc'] ?? $descRaw));
                                            }
                                        }
                                        $currencyKey = (string)($h['currency'] ?? '');
                                        $debtVal = (float)($h['debt_original'] ?? 0);
                                        $creditVal = (float)($h['credit_original'] ?? 0);
                                        $amountClass = '';
                                        $amountVal = 0.0;
                                        if ($debtVal > 0.0001) {
                                            $amountVal = $debtVal;
                                            $amountClass = 'txt-neg';
                                        } elseif ($creditVal > 0.0001) {
                                            $amountVal = $creditVal;
                                            $amountClass = 'txt-pos';
                                        } else {
                                            $amountVal = (float)($h['transaction_amount'] ?? 0);
                                            $eventTypeNorm = strtoupper(trim((string)($h['event_type'] ?? '')));
                                            if (strpos($eventTypeNorm, 'ALACAK') !== false || strpos($eventTypeNorm, 'TAHSILAT') !== false || strpos($eventTypeNorm, 'GIRIS') !== false) {
                                                $amountClass = 'txt-pos';
                                            } elseif (strpos($eventTypeNorm, 'BORC') !== false || strpos($eventTypeNorm, 'ODEME') !== false || strpos($eventTypeNorm, 'CIKIS') !== false) {
                                                $amountClass = 'txt-neg';
                                            }
                                        }
                                        ?>
                                        <tr>
                                            <td><?= e(trDate((string)$h['event_date'])) ?></td>
                                            <td><?= e($h['event_type'] ?? '') ?></td>
                                            <td class="<?= e($amountClass) ?>"><?= e(formatAmountByType($amountVal, ($currencyKey !== '' ? $currencyKey : 'TRY'), $SUPPORTED_CURRENCIES)) ?></td>
                                            <td><?= e($currencyKey) ?></td>
                                            <td><?= e($descDisplay) ?></td>
                                            <td>
                                                <a class="btn" href="<?= e(toUrl(array_merge($customerRouteBase, ['op' => $selectedOp, 'edit_event_id' => (string)($h['id'] ?? '')]))) ?>"><?= e(ui_text('edit', 'Düzenle', 'Edit')) ?></a>
                                                <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_selected_transaction', 'Seçili hareket silinsin mi?', 'Delete the selected transaction?')) ?>');">
                                                    <input type="hidden" name="action" value="customer_event_delete">
                                                    <input type="hidden" name="event_id" value="<?= e($h['id'] ?? '') ?>">
                                                    <input type="hidden" name="person_name" value="<?= e($selectedPerson) ?>">
                                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                    <input type="hidden" name="return_person" value="<?= e($selectedPerson) ?>">
                                                    <input type="hidden" name="return_op" value="<?= e($selectedOp) ?>">
                                                    <button class="btnd" type="submit"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                                </form>
                                            </td>
                                        </tr>
                                    <?php endforeach; ?>
                                <?php endif; ?>
                                </tbody>
                            </table>
                        </div>
                    <?php endif; ?>
                <?php elseif ($sub === 'services-jobs'): ?>
                    <?php
                    $serviceStatuses = ['Alindi', 'Islemde', 'Hazir', 'Teslim Edildi'];
                    
                    // Tab (Sekme) mantığı için aktif tanımı bul
                    $activeDefId = trim((string)($_GET['def_id'] ?? ''));
                    if ($activeDefId === '' && count($serviceDefinitionRows) > 0) {
                        $activeDefId = (string)$serviceDefinitionRows[0]['id'];
                    }
                    $activeDef = null;
                    foreach ($serviceDefinitionRows as $def) {
                        if ((string)$def['id'] === $activeDefId) {
                            $activeDef = $def;
                            break;
                        }
                    }
                    
                    // Dinamik kolonlar? ??z (Masa?st? Python ile uyumlu)
                    $customFields = $activeDef ? serviceDefinitionCustomFields($activeDef) : [];
                    ?>

                    <div class="row" style="justify-content: space-between; margin-bottom: 16px;">
                        <h2 style="margin:0;"><?= e(ui_text('services_workshop_ops', 'Hizmetler & Atölye İşlemleri', 'Services & Workshop Operations')) ?></h2>
                        <div class="row">
                            <button class="btnp" onclick="document.getElementById('new-def-panel').classList.toggle('hidden')">
                                <?= e(ui_text('new_service_type', 'Yeni Hizmet Türü Tanımla', 'Define New Service Type')) ?>
                            </button>
                            <?php if ($activeDef): ?>
                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_service_type', 'Bu hizmet türünü ve içindeki tüm kayıtları silmek istediğinize emin misiniz?', 'Are you sure you want to delete this service type and all of its records?')) ?>');">
                                <input type="hidden" name="action" value="service_definition_delete">
                                <input type="hidden" name="service_def_id" value="<?= e($activeDef['id']) ?>">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <button class="btnd" type="submit"><?= e(ui_text('delete_service_type_btn', 'Hizmet Türünü Sil', 'Delete Service Type')) ?></button>
                            </form>
                            <?php endif; ?>
                        </div>
                    </div>

                    <!-- Hizmet Tanımlama Formu (Gizli/Açılır Modal Benzeri) -->
                    <div id="new-def-panel" class="panel hidden" style="margin:0 0 20px 0; background: var(--bgalt); border: 1px dashed var(--primary);">
                        <h3 style="color: var(--primary);"><?= e(ui_text('new_service_type', 'Yeni Hizmet Türü Tanımla', 'Define New Service Type')) ?></h3>
                        <form method="post" class="fg compact-grid">
                            <input type="hidden" name="action" value="service_definition_add">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                            <div class="f"><label><?= e(ui_text('service_name', 'Hizmet Adı', 'Service Name')) ?></label><input type="text" name="service_def_name" placeholder="<?= e(ui_text('service_name_ph', 'Örn: Tamir, Özel Sipariş, Pırlanta Mıhlama', 'Example: Repair, Custom Order, Diamond Setting')) ?>" required></div>
                            <div class="f span-2"><label><?= e(ui_text('web_custom_fields', 'Özel Alanlar (Kolonlar)', 'Custom Fields (Columns)')) ?></label><input type="text" name="service_custom_columns" placeholder="<?= e(ui_text('web_custom_fields_ph', 'Virgülle ayırın. Örn: IMEI, Marka, Arıza', 'Separate with commas. Example: IMEI, Brand, Fault')) ?>"></div>
                            <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('web_save_definition', 'Tanımı Kaydet', 'Save Definition')) ?></button></div>
                        </form>
                    </div>

                    <?php if (!$serviceDefinitionRows): ?>
                        <div class="panel">
                            <p class="muted"><?= e(ui_text('web_define_service_first', 'Lütfen önce sağ üstteki butondan bir hizmet türü tanımlayın.', 'Please define a service type first using the button on the top right.')) ?></p>
                        </div>
                    <?php else: ?>
                        <!-- SEKMELER (TABS) -->
                        <div class="tabs op-tabs" style="margin-bottom: 20px;">
                            <?php foreach ($serviceDefinitionRows as $d): ?>
                                <a class="<?= ((string)$d['id'] === $activeDefId ? 'active' : '') ?>" href="?cat=services&sub=services-jobs&def_id=<?= e($d['id']) ?>"><?= e($d['name']) ?></a>
                            <?php endforeach; ?>
                        </div>

                        <?php if ($activeDef): ?>
                            <!-- FORM: Yeni Kay?t (?stte) -->
                            <div class="panel" style="margin-bottom: 20px; padding: 24px;">
                                <h3 style="margin-bottom: 16px; color: var(--primary); border-bottom: 1px solid var(--line); padding-bottom: 8px;">
                                    <?= e(ui_text('new', 'Yeni', 'New')) ?> <?= e($activeDef['name']) ?> <?= e(ui_text('record_suffix', 'Kaydı', 'Record')) ?>
                                </h3>
                                <form method="post" class="fg compact-grid" style="row-gap: 16px;">
                                    <input type="hidden" name="action" value="service_job_add">
                                    <input type="hidden" name="service_def_id" value="<?= e($activeDef['id']) ?>">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    
                                    <div class="f span-2">
                                        <label><?= e(ui_text('customer_name', 'Müşteri Adı', 'Customer Name')) ?></label>
                                        <?php
                                        $personNameList = [];
                                        foreach ($persons as $p) {
                                            $pn = trim((string)($p['full_name'] ?? ''));
                                            if ($pn !== '') $personNameList[] = $pn;
                                        }
                                        ?>
                                        <div class="autocomplete" data-autocomplete data-options='<?= e(json_encode($personNameList, JSON_UNESCAPED_UNICODE)) ?>'>
                                            <input type="text" data-autocomplete-input value="" placeholder="<?= e(ui_text('customer_name_select_ph', 'Müşteri adı yazın veya seçin...', 'Type or select customer name...')) ?>" autocomplete="off" required>
                                            <input type="hidden" name="customer_name" data-autocomplete-hidden value="" required>
                                            <div class="autocomplete-list" data-autocomplete-list></div>
                                        </div>
                                    </div>
                                    
                                    <div class="f span-2"><label><?= e(ui_text('description', 'Açıklama', 'Description')) ?></label><input type="text" name="description" placeholder="<?= e(ui_text('web_service_description_ph', 'Genel açıklama...', 'General description...')) ?>"></div>
                                    <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="start_date" value="<?= e(date('Y-m-d')) ?>" required></div>
                                    
                                    <div class="f">
                                        <label><?= e(ui_text('estimated_fee', 'Tahmini Ücret', 'Estimated Fee')) ?></label>
                                        <div class="row-tools" style="display:flex;">
                                            <input type="text" name="fee" placeholder="0,00" style="flex:1;">
                                            <select name="fee_currency" style="width:80px;">
                                                <?php foreach ($SUPPORTED_CURRENCIES as $c): ?>
                                                    <option value="<?= e($c) ?>"><?= e($c) ?></option>
                                                <?php endforeach; ?>
                                            </select>
                                        </div>
                                    </div>
                                    
                                    <!-- Dinamik Alanlar -->
                                    <?php foreach ($customFields as $lbl): ?>
                                        <div class="f">
                                            <label><?= e($lbl) ?></label>
                                            <input type="hidden" name="service_dynamic_label[]" value="<?= e($lbl) ?>">
                                            <input type="text" name="service_dynamic_value[]" placeholder="<?= e($lbl) ?> <?= e(ui_text('enter_suffix', 'giriniz...', 'enter...')) ?>">
                                        </div>
                                    <?php endforeach; ?>
                                    
                                    <div class="f"><label>&nbsp;</label><button class="btnp" type="submit" style="height: 42px;"><?= e(ui_text('add', 'Ekle', 'Add')) ?></button></div>
                                </form>
                            </div>

                            <!-- TABLO: Kay?tlar (Altta) -->
                            <div class="panel">
                                <div class="row" style="justify-content: space-between; margin-bottom: 16px;">
                                    <h3><?= e($activeDef['name']) ?> <?= e(ui_text('tracking_module', 'Takip Modülü', 'Tracking Module')) ?></h3>
                                </div>
                                <div class="tbl">
                                    <table>
                                        <thead>
                                            <tr>
                                                <th><?= e(ui_text('customer_name', 'Müşteri Adı', 'Customer Name')) ?></th>
                                                <th><?= e(ui_text('description', 'Açıklama', 'Description')) ?></th>
                                                <th><?= e(ui_text('date', 'Tarih', 'Date')) ?></th>
                                                <th><?= e(ui_text('estimated_fee', 'Tahmini Ücret', 'Estimated Fee')) ?></th>
                                                <?php foreach ($customFields as $lbl): ?>
                                                    <th><?= e($lbl) ?></th>
                                                <?php endforeach; ?>
                                                <th><?= e(ui_text('status', 'Durum', 'Status')) ?></th>
                                                <th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            <?php 
                                            $hasJobs = false;
                                            foreach ($serviceJobRows as $r): 
                                                if ((string)$r['service_def_id'] !== $activeDefId) continue;
                                                $hasJobs = true;
                                                $dyn = $r['dynamic_payload']['custom'] ?? [];
                                                $paymentSummary = is_array($r['payment_summary'] ?? null) ? $r['payment_summary'] : ['paid_total' => 0.0, 'remaining' => (float)($r['fee'] ?? 0)];
                                            ?>
                                            <tr>
                                                <td><strong><?= e($r['customer_name'] ?? '') ?></strong></td>
                                                <td><span class="muted"><?= e($r['description'] ?? '-') ?></span></td>
                                                <td><?= e(trDate((string)($r['start_date'] ?? ''))) ?></td>
                                                <td>
                                                    <strong><?= e(number_format((float)($r['fee'] ?? 0), 2, ',', '.')) ?> <?= e($r['currency'] ?? 'TRY') ?></strong>
                                                    <?php if ($paymentSummary['paid_total'] > 0): ?>
                                                        <br><span class="muted" style="font-size:11px;"><?= e(ui_text('remaining', 'Kalan', 'Remaining')) ?>: <?= e(number_format((float)($paymentSummary['remaining'] ?? 0), 2, ',', '.')) ?></span>
                                                    <?php endif; ?>
                                                </td>
                                                <?php foreach ($customFields as $lbl): ?>
                                                    <td><?= e($dyn[$lbl] ?? '-') ?></td>
                                                <?php endforeach; ?>
                                                <td>
                                                    <?php
                                                        $serviceStatusLabels = [
                                                            'Alindi' => ui_text('repair_status_received', 'Alındı', 'Received'),
                                                            'Alındı' => ui_text('repair_status_received', 'Alındı', 'Received'),
                                                            'Islemde' => ui_text('repair_status_in_progress', 'İşlemde', 'In Progress'),
                                                            'İşlemde' => ui_text('repair_status_in_progress', 'İşlemde', 'In Progress'),
                                                            'Hazir' => ui_text('repair_status_ready', 'Hazır', 'Ready'),
                                                            'Hazır' => ui_text('repair_status_ready', 'Hazır', 'Ready'),
                                                            'Teslim Edildi' => ui_text('repair_status_delivered', 'Teslim Edildi', 'Delivered'),
                                                        ];
                                                        $st = $r['status'] ?? '';
                                                        $stClass = ($st === 'Teslim Edildi') ? 'closed' : (($st === 'Hazir' || $st === 'Hazır') ? 'open' : 'pending');
                                                        $stLabel = $serviceStatusLabels[$st] ?? $st;
                                                    ?>
                                                    <span class="status-pill <?= $stClass ?>"><?= e($stLabel) ?></span>
                                                </td>
                                                <td style="min-width: 140px;">
                                                    <form method="post" class="inline" style="margin-bottom: 6px;">
                                                        <input type="hidden" name="action" value="service_job_status">
                                                        <input type="hidden" name="service_job_id" value="<?= e($r['id'] ?? '') ?>">
                                                        <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                        <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                        <select name="status" style="width: 100%; padding: 6px; font-size:13px; border-radius: 6px; margin-bottom: 6px;" onchange="this.form.submit()">
                                                            <?php foreach ($serviceStatuses as $serviceStatus): ?>
                                                                <option value="<?= e($serviceStatus) ?>" <?= (($r['status'] ?? '') === $serviceStatus ? 'selected' : '') ?>><?= e($serviceStatusLabels[$serviceStatus] ?? $serviceStatus) ?></option>
                                                            <?php endforeach; ?>
                                                        </select>
                                                    </form>
                                                    
                                                    <button 
                                                        type="button" 
                                                        class="btn service-collect-open"
                                                        style="width: 100%; padding: 6px; font-size:12px; margin-bottom: 6px;"
                                                        data-module-type="service"
                                                        data-record-id="<?= e($r['id'] ?? '') ?>"
                                                        data-service-job-id="<?= e($r['id'] ?? '') ?>"
                                                        data-service-name="<?= e($r['service_name'] ?? '-') ?>"
                                                        data-service-customer="<?= e($r['customer_name'] ?? '') ?>"
                                                        data-service-currency="<?= e($r['currency'] ?? 'TRY') ?>"
                                                        data-service-remaining="<?= e(number_format((float)($paymentSummary['remaining'] ?? 0), 2, '.', '')) ?>"
                                                        <?= ($st === 'Teslim Edildi' ? 'disabled' : '') ?>
                                                    ><?= e(ui_text('collect_and_deliver', 'Tahsilat & Teslim', 'Collect & Deliver')) ?></button>
                                                    
                                                    <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_service_record', 'Hizmet kaydı silinsin mi?', 'Delete service record?')) ?>');">
                                                        <input type="hidden" name="action" value="service_job_delete">
                                                        <input type="hidden" name="service_job_id" value="<?= e($r['id'] ?? '') ?>">
                                                        <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                        <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                        <button class="btnd" type="submit" style="width: 100%; padding: 6px; font-size:12px;"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                                    </form>
                                                </td>
                                            </tr>
                                            <?php endforeach; ?>
                                            <?php if (!$hasJobs): ?>
                                                <tr><td colspan="<?= 7 + count($customFields) ?>" class="muted" style="text-align:center; padding: 20px;"><?= e(ui_text('web_service_no_records', 'Bu hizmet türüne ait kayıt bulunamadı.', 'No records found for this service type.')) ?></td></tr>
                                            <?php endif; ?>
                                        </tbody>
                                    </table>
                                </div>
                            </div>
                            
                                                        <!-- Gelişmiş Tahsilat Modalı -->
                            <dialog id="service-collect-dialog" style="width: 90vw; max-width: 600px;">
                                <form method="post" id="service_collect_form" class="fg compact-grid">
                                    <input type="hidden" name="action" value="advanced_collect_and_deliver">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="module_type" id="service_collect_module_type" value="service">
                                    <input type="hidden" name="record_id" id="service_collect_record_id" value="">
                                    <input type="hidden" name="paid_currency" id="service_collect_currency_hidden" value="TRY">
                                    <input type="hidden" name="paid_amount" id="service_collect_final_amount" value="0">
                                    
                                    <h3 style="margin-top:0; grid-column: span 4; color: var(--primary);"><?= e(ui_text('service_repair_collection', 'Hizmet/Tamir Tahsilatı', 'Service/Repair Collection')) ?></h3>
                                    
                                    <div class="f span-2">
                                        <label><?= e(ui_text('customer', 'Müşteri', 'Customer')) ?></label>
                                        <div class="tag" id="service_collect_customer" style="font-size:14px; padding:5px;">-</div>
                                    </div>
                                    <div class="f span-2">
                                        <label><?= e(ui_text('total_debt_remaining', 'Toplam Borç / Kalan', 'Total Debt / Remaining')) ?></label>
                                        <div class="tag txt-neg" id="service_collect_remaining" style="font-size:16px; padding:5px; font-weight:bold;">0.00</div>
                                    </div>

                                    <div class="f span-4" style="border-top: 1px solid var(--line); margin-top: 10px; padding-top: 10px;">
                                        <div class="row" style="justify-content:space-between; margin-bottom:5px;">
                                            <strong><?= e(ui_text('payments', 'Ödemeler', 'Payments')) ?></strong>
                                            <button type="button" id="service_add_payment_btn" class="btn"><?= e(ui_text('add_payment', 'Ödeme Ekle', 'Add Payment')) ?></button>
                                        </div>
                                        <div id="service_payment_rows"></div>
                                        
                                        <!-- Özet -->
                                        <div class="row" style="justify-content:flex-end; margin-top:10px; font-size:16px;">
                                            <span><?= e(ui_text('paid_total', 'Ödenen Toplam', 'Paid Total')) ?>: </span>
                                            <strong id="service_paid_total" class="txt-pos" style="margin-left:10px;">0.00</strong>
                                        </div>
                                    </div>

                                    <div class="f span-4"><label><?= e(ui_text('description_optional', 'Açıklama (Opsiyonel)', 'Description (Optional)')) ?></label><input type="text" name="payment_note" placeholder="<?= e(ui_text('payment_note_example', 'Örn: Kredi kartı ile tahsil edildi...', 'Example: Collected by credit card...')) ?>"></div>
                                    
                                    <div class="f span-4 row" style="gap:8px; justify-content:flex-end; margin-top: 10px;">
                                        <button type="button" class="btn" id="service_collect_cancel"><?= e(ui_text('cancel', 'İptal', 'Cancel')) ?></button>
                                        <button type="submit" class="btnp" id="service_collect_submit_btn"><?= e(ui_text('collect_and_close_delivery', 'Tahsil Et & Teslimi Kapat', 'Collect & Close Delivery')) ?></button>
                                    </div>
                                </form>
                            </dialog>

                            <script>
                                (function () {
                                    const dialog = document.getElementById('service-collect-dialog');
                                    const cancelBtn = document.getElementById('service_collect_cancel');
                                    const addPaymentBtn = document.getElementById('service_add_payment_btn');
                                    const paymentRowsContainer = document.getElementById('service_payment_rows');
                                    const paidTotalEl = document.getElementById('service_paid_total');
                                    const finalAmountInput = document.getElementById('service_collect_final_amount');
                                    const form = document.getElementById('service_collect_form');
                                    
                                    let targetRemaining = 0;
                                    let targetCurrency = 'TRY';

                                    const accountsOptions = `
                                        <option value=""><?= e(ui_text('select_account', 'Hesap Seçin...', 'Select Account...')) ?></option>
                                        <?php foreach ($accounts as $a): ?>
                                            <option value="<?= e($a['id']) ?>"><?= e(($a['type'] === 'cash' ? 'Kasa' : 'Kart') . ' - ' . $a['name']) ?></option>
                                        <?php endforeach; ?>
                                    `;

                                    function calculateServiceTotal() {
                                        let total = 0;
                                        paymentRowsContainer.querySelectorAll('.svc-pay-amount').forEach(inp => {
                                            const val = Number(inp.value.replace(',', '.')) || 0;
                                            total += val;
                                        });
                                        paidTotalEl.textContent = total.toFixed(2) + ' ' + targetCurrency;
                                        finalAmountInput.value = total.toFixed(2);
                                        
                                        const submitBtn = document.getElementById('service_collect_submit_btn');
                                        if (total < targetRemaining) {
                                            submitBtn.textContent = `<?= e(ui_text('partial_collect', 'Kısmi Tahsil Et', 'Collect Partially')) ?> (${total.toFixed(2)})`;
                                            submitBtn.style.background = "var(--warning)";
                                        } else {
                                            submitBtn.textContent = <?= json_encode(ui_text('collect_and_close_delivery', 'Tahsil Et & Teslimi Kapat', 'Collect & Close Delivery'), JSON_UNESCAPED_UNICODE) ?>;
                                            submitBtn.style.background = "var(--primary)";
                                        }
                                    }

                                    function createServicePaymentRow(amount = 0) {
                                        const row = document.createElement('div');
                                        row.className = 'row';
                                        row.style.marginBottom = '6px';
                                        row.innerHTML = `
                                            <select name="service_pay_account[]" required style="flex:2;">${accountsOptions}</select>
                                            <input type="text" name="service_pay_amount[]" class="svc-pay-amount" value="${amount > 0 ? amount.toFixed(2).replace('.',',') : ''}" placeholder="0,00" required style="flex:1;">
                                            <button type="button" class="btnd svc-remove-row"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                        `;
                                        row.querySelector('.svc-remove-row').addEventListener('click', () => {
                                            row.remove();
                                            calculateServiceTotal();
                                        });
                                        row.querySelector('.svc-pay-amount').addEventListener('input', calculateServiceTotal);
                                        paymentRowsContainer.appendChild(row);
                                        calculateServiceTotal();
                                    }

                                    if (addPaymentBtn) {
                                        addPaymentBtn.addEventListener('click', () => createServicePaymentRow());
                                    }

                                    if (cancelBtn && dialog) {
                                        cancelBtn.addEventListener('click', function () {
                                            if (typeof dialog.close === 'function') dialog.close();
                                            else dialog.removeAttribute('open');
                                        });
                                    }

                                    document.querySelectorAll('.service-collect-open').forEach(function (btn) {
                                        btn.addEventListener('click', function () {
                                            targetCurrency = btn.getAttribute('data-service-currency') || 'TRY';
                                            targetRemaining = Number(btn.getAttribute('data-service-remaining') || 0);
                                            
                                            // Modül türünü ayarla (repair veya service)
                                            let modType = btn.getAttribute('data-module-type') || 'service';
                                            let hiddenAction = document.querySelector('#service_collect_form input[name="action"]');
                                            if(hiddenAction) hiddenAction.value = 'advanced_collect_and_deliver';

                                            let hiddenMod = document.getElementById('service_collect_module_type');
                                            if(!hiddenMod) {
                                                hiddenMod = document.createElement('input');
                                                hiddenMod.type = 'hidden';
                                                hiddenMod.name = 'module_type';
                                                hiddenMod.id = 'service_collect_module_type';
                                                document.getElementById('service_collect_form').appendChild(hiddenMod);
                                            }
                                            hiddenMod.value = modType;

                                            // İlgili ID'yi ata
                                            let hiddenRec = document.getElementById('service_collect_record_id');
                                            if(!hiddenRec) {
                                                hiddenRec = document.createElement('input');
                                                hiddenRec.type = 'hidden';
                                                hiddenRec.name = 'record_id';
                                                hiddenRec.id = 'service_collect_record_id';
                                                document.getElementById('service_collect_form').appendChild(hiddenRec);
                                            }
                                            hiddenRec.value = btn.getAttribute('data-record-id') || btn.getAttribute('data-service-job-id') || '';
                                            
                                            document.getElementById('service_collect_customer').textContent = btn.getAttribute('data-service-customer') || '';
                                            document.getElementById('service_collect_remaining').textContent = targetRemaining.toFixed(2) + ' ' + targetCurrency;
                                            document.getElementById('service_collect_currency_hidden').value = targetCurrency;
                                            
                                            paymentRowsContainer.innerHTML = '';
                                            if (targetRemaining > 0) {
                                                createServicePaymentRow(targetRemaining);
                                            }
                                            
                                            if (dialog && typeof dialog.showModal === 'function') dialog.showModal();
                                            else if (dialog) dialog.setAttribute('open', 'open');
                                        });
                                    });
                                })();
                            </script>
                        <?php endif; ?>
                    <?php endif; ?>

                <?php elseif ($sub === 'services-repair'): ?>
                    <?php
                    $personNameList = [];
                    foreach ($persons as $p) {
                        $pn = trim((string)($p['full_name'] ?? ''));
                        if ($pn !== '') $personNameList[] = $pn;
                    }
                    ?>
                    <div class="panel" style="margin:0 0 12px">
                        <h3><?= e(ui_text('new_repair_record', 'Yeni Tamir Kaydı', 'New Repair Record')) ?></h3>
                        <form method="post" class="fg compact-grid">
                            <input type="hidden" name="action" value="repair_add">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">

                            <div class="f span-2">
                                <label><?= e(ui_text('customer_name', 'Müşteri Adı', 'Customer Name')) ?></label>
                                <div class="autocomplete" data-autocomplete data-options='<?= e(json_encode($personNameList, JSON_UNESCAPED_UNICODE)) ?>'>
                                    <input type="text" data-autocomplete-input value="" placeholder="<?= e(ui_text('customer_name_select_ph', 'Müşteri adı yazın veya seçin...', 'Type or select customer name...')) ?>" autocomplete="off" required>
                                    <input type="hidden" name="customer_name" data-autocomplete-hidden value="" required>
                                    <div class="autocomplete-list" data-autocomplete-list></div>
                                </div>
                            </div>
                            <div class="f span-2"><label><?= e(ui_text('repair_product_description', 'Ürün Açıklaması', 'Product Description')) ?></label><input type="text" name="product_description" required placeholder="<?= e(ui_text('repair_product_description_ph', 'Örn: 14 Ayar Yüzük', 'Example: 14K Ring')) ?>"></div>

                            <div class="f span-2"><label><?= e(ui_text('repair_complaint_request', 'Şikayet / Talep', 'Complaint / Request')) ?></label><textarea name="complaint_request" rows="2" placeholder="<?= e(ui_text('repair_complaint_request_ph', 'Yapılacak işlem...', 'Requested work...')) ?>"></textarea></div>

                            <div class="f">
                                <label><?= e(ui_text('estimated_fee', 'Tahmini Ücret', 'Estimated Fee')) ?></label>
                                <div class="row-tools" style="display:flex;">
                                    <input type="text" name="estimated_fee" placeholder="0,00" style="flex:1;">
                                    <select name="fee_currency" style="width:80px;">
                                        <?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>"><?= e($c) ?></option><?php endforeach; ?>
                                    </select>
                                </div>
                            </div>

                            <div class="f"><label><?= e(ui_text('repair_received_date', 'Tarih', 'Date')) ?></label><input type="date" name="receipt_date" value="<?= e(date('Y-m-d')) ?>"></div>

                            <div class="f span-4"><label>&nbsp;</label><button class="btnp" type="submit" style="height:40px;"><?= e(ui_text('add_repair_record', 'Tamir Kaydını Ekle', 'Add Repair Record')) ?></button></div>
                        </form>
                    </div>
                    <div class="tbl">
                        <table>
                            <thead><tr><th><?= e(ui_text('customer', 'Müşteri', 'Customer')) ?></th><th><?= e(ui_text('product', 'Ürün', 'Product')) ?></th><th><?= e(ui_text('date', 'Tarih', 'Date')) ?></th><th><?= e(ui_text('status', 'Durum', 'Status')) ?></th><th><?= e(ui_text('fee', 'Ücret', 'Fee')) ?></th><th><?= e(ui_text('note', 'Not', 'Note')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$repairRows): ?>
                                <tr><td colspan="7" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($repairRows as $r): ?>
                                    <tr>
                                        <td><?= e($r['customer_full_name'] ?? '') ?></td>
                                        <td><?= e($r['product_description'] ?? '') ?></td>
                                        <td><?= e(trDate((string)($r['receipt_date'] ?? ''))) ?></td>
                                        <?php $repairStatusLabels = ['Alındı' => ui_text('repair_status_received', 'Alındı', 'Received'), 'İşlemde' => ui_text('repair_status_in_progress', 'İşlemde', 'In Progress'), 'Hazır' => ui_text('repair_status_ready', 'Hazır', 'Ready'), 'Teslim Edildi' => ui_text('repair_status_delivered', 'Teslim Edildi', 'Delivered')]; ?>
                                        <td><?= e($repairStatusLabels[$r['status'] ?? ''] ?? ($r['status'] ?? '')) ?></td>
                                        <td><?= e(number_format((float)($r['estimated_fee'] ?? 0), 2, ',', '.')) ?> <?= e($r['fee_currency'] ?? 'TRY') ?></td>
                                        <td><?= e($r['notes'] ?? '') ?></td>
                                        <td style="min-width: 140px;">
                                            <form method="post" class="inline" style="margin-bottom: 6px;">
                                                <input type="hidden" name="action" value="repair_status">
                                                <input type="hidden" name="repair_id" value="<?= e($r['id'] ?? '') ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <select name="status" style="width: 100%; padding: 6px; font-size:13px; border-radius: 6px; margin-bottom: 6px;" onchange="this.form.submit()">
                                                    <?php foreach (['Alındı', 'İşlemde', 'Hazır', 'Teslim Edildi'] as $st): ?>
                                                        <option value="<?= e($st) ?>" <?= (($r['status'] ?? '') === $st ? 'selected' : '') ?>><?= e($repairStatusLabels[$st] ?? $st) ?></option>
                                                    <?php endforeach; ?>
                                                </select>
                                            </form>
                                            
                                            <!-- YENİ GELİŞMİŞ TAHSİLAT BUTONU -->
                                            <button 
                                                type="button" 
                                                class="btn service-collect-open"
                                                style="width: 100%; padding: 6px; font-size:12px; margin-bottom: 6px;"
                                                data-module-type="repair"
                                                data-record-id="<?= e($r['id'] ?? '') ?>"
                                                data-service-name="<?= e($r['product_description'] ?? '-') ?>"
                                                data-service-customer="<?= e($r['customer_full_name'] ?? '') ?>"
                                                data-service-currency="<?= e($r['fee_currency'] ?? 'TRY') ?>"
                                                data-service-remaining="<?= e(number_format((float)($r['estimated_fee'] ?? 0), 2, '.', '')) ?>"
                                                <?= (($r['status'] ?? '') === 'Teslim Edildi' ? 'disabled' : '') ?>
                                            ><?= e(ui_text('collect_and_deliver', 'Tahsilat & Teslim', 'Collect & Deliver')) ?></button>

                                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_repair_record', 'Tamir kaydı silinsin mi?', 'Delete repair record?')) ?>');">
                                                <input type="hidden" name="action" value="repair_delete">
                                                <input type="hidden" name="repair_id" value="<?= e($r['id'] ?? '') ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button class="btnd" type="submit" style="width: 100%; padding: 6px; font-size:12px;"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'tools-reports'): ?>
                    <?php
                    $reportTypeUi = trim((string)($_GET['report_type'] ?? 'cashflow'));
                    if ($reportTypeUi === 'stock') {
                        $reportTypeUi = 'stock_product';
                    }
                    $reportTypeOptions = [
                        'cashflow' => ui_text('web_report_cashflow', 'Nakit Akışı', 'Cash Flow'),
                        'profit' => ui_text('web_report_profit', 'Kar / Zarar', 'Profit / Loss'),
                        'stock_product' => ui_text('product_status_tab', 'Altın Stok', 'Gold Stock'),
                        'stock_scrap' => ui_text('scrap_status_tab', 'Hurda Stok', 'Scrap Stock'),
                        'stock_ziynet' => ui_text('ziynet_status_tab', 'Ziynet Stok', 'Ziynet Stock'),
                        'customer_statement' => ui_text('web_report_customer_statement', 'Müşteri Ekstresi', 'Customer Statement'),
                        'service_general' => ui_text('web_report_service_general', 'Genel Hizmet', 'General Service'),
                        'tax' => ui_text('web_report_tax', 'Vergi Raporu', 'Tax Report'),
                    ];
                    if (!isset($reportTypeOptions[$reportTypeUi])) {
                        $reportTypeUi = 'cashflow';
                    }
                    $reportStartUi = normDate((string)($_GET['report_start'] ?? date('Y-m-01')));
                    $reportEndUi = normDate((string)($_GET['report_end'] ?? date('Y-m-d')));
                    $reportCustomerUi = trim((string)($_GET['report_customer'] ?? ''));
                    $reportDepotUi = trim((string)($_GET['report_depot'] ?? ''));
                    $showCustomerFilter = ($reportTypeUi === 'customer_statement');
                    $showDepotFilter = in_array($reportTypeUi, ['stock_product', 'stock_scrap', 'stock_ziynet'], true);
                    $showDateFilters = !$showDepotFilter;
                    ?>
                    <div class="panel" style="margin:0 0 12px">
                        <h3><?= e(ui_text('web_report_params', 'Rapor Parametreleri', 'Report Parameters')) ?></h3>
                        <form method="get" class="fg" id="report-filter-form">
                            <input type="hidden" name="cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="sub" value="<?= e($sub) ?>">
                            <div class="f">
                                <label><?= e(ui_text('report_tab', 'Rapor Tipi', 'Report Type')) ?></label>
                                <select name="report_type" id="report_type_select">
                                    <?php foreach ($reportTypeOptions as $k => $lbl): ?>
                                        <option value="<?= e($k) ?>" <?= ($reportTypeUi === $k ? 'selected' : '') ?>><?= e($lbl) ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="f<?= ($showDateFilters ? '' : ' hidden') ?>" id="report_start_wrap"><label><?= e(ui_text('start_date', 'Başlangıç', 'Start Date')) ?></label><input type="date" name="report_start" value="<?= e($reportStartUi) ?>"></div>
                            <div class="f<?= ($showDateFilters ? '' : ' hidden') ?>" id="report_end_wrap"><label><?= e(ui_text('end_date', 'Bitiş', 'End Date')) ?></label><input type="date" name="report_end" value="<?= e($reportEndUi) ?>"></div>
                            <div class="f<?= ($showCustomerFilter ? '' : ' hidden') ?>" id="report_customer_wrap">
                                <label><?= e(ui_text('customer', 'Müşteri', 'Customer')) ?></label>
                                <select name="report_customer">
                                    <option value=""><?= e(ui_text('all_customers', 'Tüm Müşteriler', 'All Customers')) ?></option>
                                    <?php foreach ($persons as $p): ?>
                                        <?php $pn = trim((string)($p['full_name'] ?? '')); if ($pn === '') { continue; } ?>
                                        <option value="<?= e($pn) ?>" <?= ($reportCustomerUi === $pn ? 'selected' : '') ?>><?= e($pn) ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="f<?= ($showDepotFilter ? '' : ' hidden') ?>" id="report_depot_wrap">
                                <label><?= e(ui_text('web_stock_depots', 'Depo', 'Depot')) ?></label>
                                <select name="report_depot">
                                    <option value=""><?= e(ui_text('all_depots', 'Tüm Depolar', 'All Depots')) ?></option>
                                    <?php foreach ($depotNames as $dn): ?>
                                        <?php $dnText = trim((string)$dn); if ($dnText === '') { continue; } ?>
                                        <option value="<?= e($dnText) ?>" <?= ($reportDepotUi === $dnText ? 'selected' : '') ?>><?= e($dnText) ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('list', 'Listele', 'List')) ?></button></div>
                        </form>
                        <div class="row" style="margin-top:8px">
                            <form method="post" class="inline">
                                <input type="hidden" name="action" value="report_export">
                                <input type="hidden" name="export_format" value="excel">
                                <input type="hidden" name="report_type" value="<?= e($reportTypeUi) ?>">
                                <input type="hidden" name="report_start" value="<?= e($reportStartUi) ?>">
                                <input type="hidden" name="report_end" value="<?= e($reportEndUi) ?>">
                                <input type="hidden" name="report_customer" value="<?= e($reportCustomerUi) ?>">
                                <input type="hidden" name="report_depot" value="<?= e($reportDepotUi) ?>">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <button type="submit" class="btn"><?= e(ui_text('export_excel', 'Excel Export', 'Export Excel')) ?></button>
                            </form>
                            <form method="post" class="inline">
                                <input type="hidden" name="action" value="report_export">
                                <input type="hidden" name="export_format" value="pdf">
                                <input type="hidden" name="report_type" value="<?= e($reportTypeUi) ?>">
                                <input type="hidden" name="report_start" value="<?= e($reportStartUi) ?>">
                                <input type="hidden" name="report_end" value="<?= e($reportEndUi) ?>">
                                <input type="hidden" name="report_customer" value="<?= e($reportCustomerUi) ?>">
                                <input type="hidden" name="report_depot" value="<?= e($reportDepotUi) ?>">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <button type="submit" class="btn"><?= e(ui_text('print', 'PDF Export', 'Export PDF')) ?></button>
                            </form>
                        </div>
                        <script>
                            (function () {
                                var typeSel = document.getElementById('report_type_select');
                                if (!typeSel) return;
                                var wStart = document.getElementById('report_start_wrap');
                                var wEnd = document.getElementById('report_end_wrap');
                                var wCustomer = document.getElementById('report_customer_wrap');
                                var wDepot = document.getElementById('report_depot_wrap');
                                var sync = function () {
                                    var t = String(typeSel.value || '');
                                    var isStock = (t === 'stock_product' || t === 'stock_scrap' || t === 'stock_ziynet');
                                    var isCustomer = (t === 'customer_statement');
                                    if (wStart) wStart.classList.toggle('hidden', isStock);
                                    if (wEnd) wEnd.classList.toggle('hidden', isStock);
                                    if (wCustomer) wCustomer.classList.toggle('hidden', !isCustomer);
                                    if (wDepot) wDepot.classList.toggle('hidden', !isStock);
                                };
                                typeSel.addEventListener('change', sync);
                                sync();
                            })();
                        </script>
                    </div>

                    <?php if ($toolReportSummary): ?>
                        <div class="row" style="margin-bottom:8px">
                            <?php foreach ($toolReportSummary as $k => $v): ?>
                                <span class="tag"><strong><?= e((string)$k) ?>:</strong> <?= e((string)$v) ?></span>
                            <?php endforeach; ?>
                        </div>
                    <?php endif; ?>

                    <div class="tbl">
                        <table>
                            <thead>
                            <tr>
                                <?php foreach ($toolReportHeaders as $h): ?>
                                    <th><?= e((string)$h) ?></th>
                                <?php endforeach; ?>
                            </tr>
                            </thead>
                            <tbody>
                            <?php if (!$toolReportRows): ?>
                                <tr><td colspan="<?= e((string)max(1, count($toolReportHeaders))) ?>" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($toolReportRows as $row): ?>
                                    <tr>
                                        <?php foreach ($row as $col): ?>
                                            <td><?= e((string)$col) ?></td>
                                        <?php endforeach; ?>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'tools-agenda'): ?>
                    <div class="panel" style="margin:0 0 12px">
                        <h3><?= e(ui_text('agenda_tab', 'Ajanda Notu', 'Agenda Note')) ?></h3>
                        <form method="post" class="fg">
                            <input type="hidden" name="action" value="agenda_save">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                            <div class="f"><label><?= e(ui_text('date', 'Tarih', 'Date')) ?></label><input type="date" name="agenda_date" value="<?= e(date('Y-m-d')) ?>"></div>
                            <div class="f"><label><?= e(ui_text('title', 'Başlık', 'Title')) ?></label><input type="text" name="agenda_title" placeholder="<?= e(ui_text('reminders', 'Hatırlatma başlığı', 'Reminder title')) ?>"></div>
                            <div class="f"><label><?= e(ui_text('time', 'Saat', 'Time')) ?></label><input type="time" name="agenda_time"></div>
                            <div class="f"><label><?= e(ui_text('reminders', 'Hatırlat', 'Remind')) ?></label><label><input type="checkbox" name="agenda_is_reminder" value="1"> <?= e(ui_text('yes', 'Evet', 'Yes')) ?></label></div>
                            <div class="f span-4"><label><?= e(ui_text('notes', 'Not', 'Note')) ?></label><textarea name="agenda_note" placeholder="<?= e(ui_text('web_note_details', 'Not detayları', 'Note details')) ?>"></textarea></div>
                            <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                        </form>
                    </div>
                    <div class="tbl">
                        <table>
                            <thead><tr><th><?= e(ui_text('date', 'Tarih', 'Date')) ?></th><th><?= e(ui_text('title', 'Başlık', 'Title')) ?></th><th><?= e(ui_text('time', 'Saat', 'Time')) ?></th><th><?= e(ui_text('notes', 'Not', 'Note')) ?></th><th><?= e(ui_text('reminders', 'Hatırlatıcı', 'Reminder')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$agendaRows): ?>
                                <tr><td colspan="6" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($agendaRows as $r): ?>
                                    <?php $agendaPayload = is_array($r['note_payload'] ?? null) ? $r['note_payload'] : agendaDecodeNote((string)($r['note_value'] ?? '')); ?>
                                    <tr>
                                        <td><?= e(trDate((string)($r['note_key'] ?? ''))) ?></td>
                                        <td><?= e($agendaPayload['title'] !== '' ? $agendaPayload['title'] : '-') ?></td>
                                        <td><?= e($agendaPayload['time'] !== '' ? $agendaPayload['time'] : '-') ?></td>
                                        <td><?= e($agendaPayload['note_text'] !== '' ? $agendaPayload['note_text'] : '-') ?></td>
                                        <td><?= (!empty($agendaPayload['is_reminder']) ? ui_text('yes', 'Evet', 'Yes') : ui_text('no', 'Hayır', 'No')) ?></td>
                                        <td>
                                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_agenda', 'Ajanda notu silinsin mi?', 'Delete agenda note?')) ?>');">
                                                <input type="hidden" name="action" value="agenda_delete">
                                                <input type="hidden" name="agenda_date" value="<?= e($r['note_key'] ?? '') ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button class="btnd" type="submit"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'tools-search'): ?>
                    <?php $searchTermUi = trim((string)($_GET['q'] ?? '')); ?>
                    <div class="panel" style="margin:0 0 12px">
                        <h3><?= e(ui_text('perm_search', 'Genel Arama / Barkod Sorgu', 'General Search / Barcode Query')) ?></h3>
                        <form method="get" class="row">
                            <input type="hidden" name="cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="sub" value="<?= e($sub) ?>">
                            <input type="text" name="q" value="<?= e($searchTermUi) ?>" placeholder="<?= e(ui_text('quick_search', 'Ad, telefon, barkod veya ürün...', 'Name, phone, barcode or product...')) ?>" style="max-width:420px" required>
                            <button class="btnp" type="submit"><?= e(ui_text('search', 'Ara', 'Search')) ?></button>
                        </form>
                    </div>
                    <div class="tbl">
                        <table>
                            <thead><tr><th><?= e(ui_text('source', 'Kaynak', 'Source')) ?></th><th><?= e(ui_text('name', 'Ad', 'Name')) ?></th><th><?= e(ui_text('details', 'Detay', 'Details')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$toolSearchRows): ?>
                                <tr><td colspan="3" class="muted"><?= e(ui_text('no_results', 'Sonuç yok.', 'No results.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($toolSearchRows as $r): ?>
                                    <tr>
                                        <td><?= e($r['src_type'] ?? '') ?></td>
                                        <td><?= e($r['src_name'] ?? '') ?></td>
                                        <td><?= e($r['src_detail'] ?? '') ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'tools-calculator'): ?>
                    <div class="panel" style="margin:0">
                        <h3><?= e(ui_text('calculator_tab', 'Hesap Makinesi', 'Calculator')) ?></h3>
                        <div style="max-width:520px">
                            <input type="text" id="tool_calc_display" readonly style="width:100%;height:64px;font-size:28px;text-align:right;margin-bottom:10px;padding:8px 10px;border:1px solid var(--line);border-radius:12px;background:#fff">
                            <div style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px" id="tool-calc-grid">
                                <?php foreach ([
                                    ['C', 'danger'], ['<-', ''], ['(', ''], [')', ''],
                                    ['7', ''], ['8', ''], ['9', ''], ['/', ''],
                                    ['4', ''], ['5', ''], ['6', ''], ['*', ''],
                                    ['1', ''], ['2', ''], ['3', ''], ['-', ''],
                                    ['0', '', 2], ['.', ''], ['+', ''],
                                    ['=', 'primary', 4],
                                ] as $btnCfg): ?>
                                    <?php
                                    $btnText = (string)$btnCfg[0];
                                    $btnKind = (string)($btnCfg[1] ?? '');
                                    $span = (int)($btnCfg[2] ?? 1);
                                    $cls = 'btn';
                                    if ($btnKind === 'danger') {
                                        $cls = 'btnd';
                                    } elseif ($btnKind === 'primary') {
                                        $cls = 'btnp';
                                    }
                                    ?>
                                    <button type="button" class="<?= e($cls) ?>" data-calc-key="<?= e($btnText) ?>" style="height:50px;grid-column:span <?= e((string)$span) ?>"><?= e($btnText) ?></button>
                                <?php endforeach; ?>
                            </div>
                        </div>
                    </div>
                    <script>
                        (function () {
                            var display = document.getElementById('tool_calc_display');
                            var grid = document.getElementById('tool-calc-grid');
                            if (!display || !grid) return;
                            var pushKey = function (k) {
                                var key = String(k || '');
                                if (!key) return;
                                if (key === 'C') {
                                    display.value = '';
                                    return;
                                }
                                if (key === '<-') {
                                    display.value = String(display.value || '').slice(0, -1);
                                    return;
                                }
                                if (key === '=') {
                                    var txt = String(display.value || '').replace(/,/g, '.').trim();
                                    if (!/^[0-9+\-*/().\s]+$/.test(txt)) {
                                            display.value = '<?= e(ui_text('error', 'Hata', 'Error')) ?>';
                                            return;
                                        }
                                    try {
                                        var val = Function('return (' + txt + ')')();
                                        if (Number.isFinite(Number(val))) {
                                            display.value = String(val).replace('.', ',');
                                        } else {
                                            display.value = '<?= e(ui_text('error', 'Hata', 'Error')) ?>';
                                        }
                                    } catch (_e) {
                                        display.value = '<?= e(ui_text('error', 'Hata', 'Error')) ?>';
                                    }
                                    return;
                                }
                                if (String(display.value || '') === '<?= e(ui_text('error', 'Hata', 'Error')) ?>') {
                                    display.value = '';
                                }
                                display.value = String(display.value || '') + key;
                            };
                            grid.querySelectorAll('[data-calc-key]').forEach(function (btn) {
                                btn.addEventListener('click', function () { pushKey(btn.getAttribute('data-calc-key')); });
                            });
                            window.addEventListener('keydown', function (ev) {
                                var k = ev.key;
                                if (/^[0-9]$/.test(k) || ['+', '-', '*', '/', '.', '(', ')'].indexOf(k) >= 0) {
                                    ev.preventDefault();
                                    pushKey(k);
                                    return;
                                }
                                if (k === 'Enter') {
                                    ev.preventDefault();
                                    pushKey('=');
                                    return;
                                }
                                if (k === 'Backspace') {
                                    ev.preventDefault();
                                    pushKey('<-');
                                    return;
                                }
                                if (k === 'Escape') {
                                    ev.preventDefault();
                                    pushKey('C');
                                }
                            });
                        })();
                    </script>

                <?php elseif ($sub === 'tools-zakat'): ?>
                    <?php
                    $canStoreZakat = hasPerm($u, 'perm_zakat');
                    $canGuestZakat = hasPerm($u, 'perm_zakat_guest');
                    $requestedZakatTab = trim((string)($_GET['zakat_tab'] ?? ''));
                    if (!in_array($requestedZakatTab, ['store', 'guest'], true)) {
                        $requestedZakatTab = $canStoreZakat ? 'store' : 'guest';
                    }
                    $initialZakatTab = $requestedZakatTab;
                    ?>
                    <div class="panel" style="margin:0">
                        <h3><?= e(ui_text('web_zakat_title', 'Zekat Hesaplama', 'Zakat Calculator')) ?></h3>
                        <div class="seg-tabs" id="zakat-tabs" style="margin:0 0 12px">
                            <?php if ($canStoreZakat): ?>
                                <button type="button" data-ztab="store" class="<?= ($initialZakatTab === 'store' ? 'active' : '') ?>"><?= e(ui_text('store_calculation', 'Mağaza Hesaplaması', 'Store Calculation')) ?></button>
                            <?php endif; ?>
                            <?php if ($canGuestZakat): ?>
                                <button type="button" data-ztab="guest" class="<?= ($initialZakatTab === 'guest' ? 'active' : '') ?>"><?= e(ui_text('perm_zakat_guest', 'Müşteri Zekat Hesabı', 'Customer Zakat Calculation')) ?></button>
                            <?php endif; ?>
                        </div>

                        <div id="zakat_store_panel" class="<?= ($initialZakatTab === 'store' ? '' : 'hidden') ?>">
                            <?php if ($canStoreZakat): ?>
                                <form method="get" class="fg compact-grid">
                                    <input type="hidden" name="cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="zakat_tab" value="store">
                                    <div class="f"><label><?= e(ui_text('web_nisap_start_date', 'Nisap Başlangıç Tarihi', 'Nisab Start Date')) ?></label><input type="date" name="zakat_nisap_date" value="<?= e($zakatStoreDate) ?>"></div>
                                    <div class="f"><label><?= e(ui_text('to_currency', 'Hedef PB', 'Target Currency')) ?></label><input type="text" value="<?= e($zakatDisplayCurrency) ?>" readonly></div>
                                    <div class="f"><label><?= e(ui_text('web_pure_gold_price', 'Has Altın Fiyatı', 'Pure Gold Price')) ?></label><input type="text" value="<?= e(number_format((float)($zakatStoreReport['gold_price'] ?? 0), 2, ',', '.')) . ' ' . e($zakatDisplayCurrency) ?>" readonly></div>
                                    <div class="f"><label>&nbsp;</label><button type="submit" class="btnp"><?= e(ui_text('calculate_upper', 'Hesapla', 'Calculate')) ?></button></div>
                                </form>
                                <?php if (is_array($zakatStoreReport)): ?>
                                    <div class="tbl" style="margin-top:10px">
                                        <table>
                                            <thead><tr><th><?= e(ui_text('item', 'Kalem', 'Item')) ?></th><th><?= e(ui_text('value', 'Değer', 'Value')) ?></th></tr></thead>
                                            <tbody>
                                                <tr><td><?= e(ui_text('web_gross_assets', 'Brüt Varlık', 'Gross Assets')) ?></td><td><?= e(number_format((float)($zakatStoreReport['gross_asset_value'] ?? 0), 2, ',', '.')) ?> <?= e($zakatDisplayCurrency) ?></td></tr>
                                                <tr><td><?= e(ui_text('web_cash_receivables', 'Nakit ve Alacak', 'Cash and Receivables')) ?></td><td><?= e(number_format((float)($zakatStoreReport['total_cash'] ?? 0), 2, ',', '.')) ?> <?= e($zakatDisplayCurrency) ?></td></tr>
                                                <tr><td><?= e(ui_text('web_deductible_debts', 'Düşülecek Borçlar', 'Deductible Debts')) ?></td><td><?= e(number_format((float)($zakatStoreReport['total_debts'] ?? 0), 2, ',', '.')) ?> <?= e($zakatDisplayCurrency) ?></td></tr>
                                                <tr><td><?= e(ui_text('tax_base_amount', 'Net Matrah', 'Net Base')) ?></td><td><?= e(number_format((float)($zakatStoreReport['net_asset_value'] ?? 0), 2, ',', '.')) ?> <?= e($zakatDisplayCurrency) ?></td></tr>
                                                <tr><td><?= e(ui_text('web_nisap_value', 'Nisap Değeri', 'Nisab Value')) ?></td><td><?= e(number_format((float)($zakatStoreReport['nisap_value'] ?? 0), 2, ',', '.')) ?> <?= e($zakatDisplayCurrency) ?></td></tr>
                                                <tr><td><?= e(ui_text('web_zakat_due', 'Ödenecek Zekat', 'Zakat Due')) ?></td><td><?= e(number_format((float)($zakatStoreReport['zakat_due'] ?? 0), 2, ',', '.')) ?> <?= e($zakatDisplayCurrency) ?></td></tr>
                                                <tr><td><?= e(ui_text('web_hawl_status', 'Hawl Durumu', 'Hawl Status')) ?></td><td><?= e($zakatStoreReport['hawl_status'] ?? '-') ?></td></tr>
                                            </tbody>
                                        </table>
                                    </div>
                                    <div class="tbl" style="margin-top:10px">
                                        <table>
                                            <thead><tr><th><?= e(ui_text('group', 'Grup', 'Group')) ?></th><th><?= e(ui_text('item', 'Kalem', 'Item')) ?></th><th><?= e(ui_text('karat_currency', 'Ayar/Birim', 'Karat/Unit')) ?></th><th><?= e(ui_text('gross_amount', 'Brüt', 'Gross')) ?></th><th><?= e(ui_text('pure_gram', 'Has', 'Pure')) ?></th><th><?= e(ui_text('value', 'Değer', 'Value')) ?></th></tr></thead>
                                            <tbody>
                                            <?php if (!($zakatStoreReport['details'] ?? [])): ?>
                                                <tr><td colspan="6" class="muted"><?= e(ui_text('web_no_zakat_items', 'Hesaplamaya dahil kalem bulunmadı.', 'No items included in the calculation.')) ?></td></tr>
                                            <?php else: ?>
                                                <?php foreach (($zakatStoreReport['details'] ?? []) as $detailRow): ?>
                                                    <tr>
                                                        <td><?= e($detailRow['group'] ?? '') ?></td>
                                                        <td><?= e($detailRow['name'] ?? '') ?></td>
                                                        <td><?= e($detailRow['karat'] ?? '') ?></td>
                                                        <td><?= e(number_format((float)($detailRow['gross'] ?? 0), 3, ',', '.')) ?></td>
                                                        <td><?= e(number_format((float)($detailRow['pure'] ?? 0), 3, ',', '.')) ?></td>
                                                        <td><?= e(number_format((float)($detailRow['value'] ?? 0), 2, ',', '.')) ?> <?= e($zakatDisplayCurrency) ?></td>
                                                    </tr>
                                                <?php endforeach; ?>
                                            <?php endif; ?>
                                            </tbody>
                                        </table>
                                    </div>
                                <?php endif; ?>
                            <?php endif; ?>
                        </div>

                        <div id="zakat_guest_panel" class="<?= ($initialZakatTab === 'guest' ? '' : 'hidden') ?>">
                            <?php if ($canGuestZakat): ?>
                                <form method="get" class="fg compact-grid" id="zakat-guest-form">
                                    <input type="hidden" name="cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="sub" value="<?= e($sub) ?>">
                                    <input type="hidden" name="zakat_tab" value="guest">
                                    <input type="hidden" name="zakat_guest_submit" value="1">
                                    <div class="f span-4">
                                        <label><?= e(ui_text('asset_items', 'Varlık Kalemleri', 'Asset Items')) ?></label>
                                        <div id="zakat_guest_rows">
                                            <?php foreach ($zakatGuestItems as $guestRow): ?>
                                                <div class="row zakat-guest-row" style="margin-bottom:6px">
                                                    <select name="zakat_guest_type[]" class="zakat-guest-type" style="max-width:180px">
                                                        <?php foreach (['cash' => 'Nakit', 'gold' => 'Altin', 'silver' => 'Gumus', 'trade' => 'Ticari Mal'] as $guestTypeKey => $guestTypeLabel): ?>
                                                            <option value="<?= e($guestTypeKey) ?>" <?= (($guestRow['type'] ?? '') === $guestTypeKey ? 'selected' : '') ?>><?= e($guestTypeLabel) ?></option>
                                                        <?php endforeach; ?>
                                                    </select>
                                                    <input type="text" name="zakat_guest_detail[]" value="<?= e($guestRow['detail'] ?? '') ?>" placeholder="PB veya ayar" style="max-width:180px">
                                                    <input type="text" name="zakat_guest_amount[]" value="<?= e($guestRow['amount_raw'] ?? '') ?>" placeholder="0,00" style="max-width:180px">
                                                    <button type="button" class="btnd zakat-guest-remove"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                                </div>
                                            <?php endforeach; ?>
                                        </div>
                                        <button type="button" class="btn" id="zakat_guest_add_row">Kalem Ekle</button>
                                    </div>
                                    <div class="f"><label>Borc</label><input type="text" name="zakat_guest_debt" value="<?= e($zakatGuestDebt) ?>" placeholder="0,00"></div>
                                    <div class="f"><label><?= e(ui_text('debt_currency', 'Borç PB', 'Debt Currency')) ?></label><select name="zakat_guest_debt_currency"><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>" <?= ($zakatGuestDebtCurrency === $c ? 'selected' : '') ?>><?= e($c) ?></option><?php endforeach; ?></select></div>
                                    <div class="f"><label><?= e(ui_text('to_currency', 'Hedef PB', 'Target Currency')) ?></label><select name="zakat_guest_currency"><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>" <?= ($zakatGuestTargetCurrency === $c ? 'selected' : '') ?>><?= e($c) ?></option><?php endforeach; ?></select></div>
                                    <div class="f"><label>&nbsp;</label><button type="submit" class="btnp">Hesapla</button></div>
                                </form>
                                <?php if (is_array($zakatGuestResult)): ?>
                                    <div class="tbl" style="margin-top:10px">
                                        <table>
                                            <thead><tr><th>Kalem</th><th>Deger</th></tr></thead>
                                            <tbody>
                                                <tr><td>Altin Degeri</td><td><?= e(number_format((float)($zakatGuestResult['val_gold'] ?? 0), 2, ',', '.')) ?> <?= e($zakatGuestTargetCurrency) ?></td></tr>
                                                <tr><td>Gumus Degeri</td><td><?= e(number_format((float)($zakatGuestResult['val_silver'] ?? 0), 2, ',', '.')) ?> <?= e($zakatGuestTargetCurrency) ?></td></tr>
                                                <tr><td>Nakit / Ticari Mal</td><td><?= e(number_format((float)($zakatGuestResult['val_cash'] ?? 0), 2, ',', '.')) ?> <?= e($zakatGuestTargetCurrency) ?></td></tr>
                                                <tr><td>Borc</td><td><?= e(number_format((float)($zakatGuestResult['val_debt'] ?? 0), 2, ',', '.')) ?> <?= e($zakatGuestTargetCurrency) ?></td></tr>
                                                <tr><td>Net Varlik</td><td><?= e(number_format((float)($zakatGuestResult['net_assets'] ?? 0), 2, ',', '.')) ?> <?= e($zakatGuestTargetCurrency) ?></td></tr>
                                                <tr><td>Nisap</td><td><?= e(number_format((float)($zakatGuestResult['nisap_val'] ?? 0), 2, ',', '.')) ?> <?= e($zakatGuestTargetCurrency) ?></td></tr>
                                                <tr><td>Odenecek Zekat</td><td><?= e(number_format((float)($zakatGuestResult['zakat_due'] ?? 0), 2, ',', '.')) ?> <?= e($zakatGuestTargetCurrency) ?></td></tr>
                                            </tbody>
                                        </table>
                                    </div>
                                <?php endif; ?>
                            <?php endif; ?>
                        </div>
                    </div>
                    <script>
                        (function () {
                            var tabWrap = document.getElementById('zakat-tabs');
                            var storePanel = document.getElementById('zakat_store_panel');
                            var guestPanel = document.getElementById('zakat_guest_panel');
                            if (tabWrap && storePanel && guestPanel) {
                                tabWrap.querySelectorAll('button[data-ztab]').forEach(function (btn) {
                                    btn.addEventListener('click', function () {
                                        var t = String(btn.getAttribute('data-ztab') || 'store');
                                        tabWrap.querySelectorAll('button[data-ztab]').forEach(function (b) {
                                            b.classList.toggle('active', b === btn);
                                        });
                                        storePanel.classList.toggle('hidden', t !== 'store');
                                        guestPanel.classList.toggle('hidden', t !== 'guest');
                                    });
                                });
                            }
                            var guestRows = document.getElementById('zakat_guest_rows');
                            var addRowBtn = document.getElementById('zakat_guest_add_row');
                            function addGuestRow() {
                                if (!guestRows) return;
                                var row = document.createElement('div');
                                row.className = 'row zakat-guest-row';
                                row.style.marginBottom = '6px';
                                row.innerHTML =
                                    '<select name=\"zakat_guest_type[]\" class=\"zakat-guest-type\" style=\"max-width:180px\">' +
                                    '<option value=\"cash\">Nakit</option>' +
                                    '<option value=\"gold\">Altin</option>' +
                                    '<option value=\"silver\">Gumus</option>' +
                                    '<option value=\"trade\">Ticari Mal</option>' +
                                    '</select>' +
                                    '<input type=\"text\" name=\"zakat_guest_detail[]\" placeholder=\"PB veya ayar\" style=\"max-width:180px\">' +
                                    '<input type=\"text\" name=\"zakat_guest_amount[]\" placeholder=\"0,00\" style=\"max-width:180px\">' +
                                    '<button type=\"button\" class=\"btnd zakat-guest-remove\"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>';
                                var delBtn = row.querySelector('button');
                                if (delBtn) {
                                    delBtn.addEventListener('click', function () { row.remove(); });
                                }
                                guestRows.appendChild(row);
                            }
                            if (addRowBtn) {
                                addRowBtn.addEventListener('click', addGuestRow);
                            }
                            if (guestRows) {
                                guestRows.querySelectorAll('.zakat-guest-remove').forEach(function (btn) {
                                    btn.addEventListener('click', function () {
                                        var row = btn.closest('.zakat-guest-row');
                                        if (row) {
                                            row.remove();
                                        }
                                    });
                                });
                            }
                        })();
                    </script>

                <?php elseif ($sub === 'settings-general'): ?>
                    <?php
                    $builtinThemeMap = themeCatalogBuiltin();
                    $customThemeMap = decodeThemeCollectionFromJson((string)($generalSettings['custom_themes_json'] ?? ''));
                    $themeOptions = array_keys($builtinThemeMap);
                    $customNames = array_keys($customThemeMap);
                    natcasesort($customNames);
                    foreach ($customNames as $cn) {
                        if (!in_array($cn, $themeOptions, true)) {
                            $themeOptions[] = $cn;
                        }
                    }
                    $displayCur = strtoupper((string)($generalSettings['display_currency'] ?? 'TRY'));
                    $currentThemeName = trim((string)($generalSettings['theme_name'] ?? 'Green Harmony'));
                    $currentLangCode = web_lang_normalize_code((string)($generalSettings['language'] ?? web_lang_code()));
                    if (!in_array($currentThemeName, $themeOptions, true)) {
                        $currentThemeName = 'Green Harmony';
                    }
                    ?>
                    <form method="post" class="fg" id="settings-general-form">
                        <input type="hidden" name="action" value="settings_general_save">
                        <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                        <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                        <div class="f"><label><?= e(ui_text('document_prefix', 'Belge Ön Eki', 'Document Prefix')) ?></label><input type="text" name="document_prefix" value="<?= e($generalSettings['document_prefix'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('shop_name', 'Mağaza Adı', 'Store Name')) ?></label><input type="text" name="shop_name" value="<?= e($generalSettings['shop_name'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('shop_phone', 'Mağaza Telefon', 'Store Phone')) ?></label><input type="text" name="shop_phone" value="<?= e($generalSettings['shop_phone'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('company_name', 'Şirket Adı', 'Company Name')) ?></label><input type="text" name="company_name" value="<?= e($generalSettings['company_name'] ?? '') ?>"></div>
                        <div class="f span-2"><label><?= e(ui_text('company_address', 'Şirket Adres', 'Company Address')) ?></label><input type="text" name="company_address" value="<?= e($generalSettings['company_address'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('tax_office', 'Vergi Dairesi', 'Tax Office')) ?></label><input type="text" name="tax_office" value="<?= e($generalSettings['tax_office'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('tax_number', 'Vergi No', 'Tax Number')) ?></label><input type="text" name="tax_number" value="<?= e($generalSettings['tax_number'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('company_phone', 'Şirket Telefon', 'Company Phone')) ?></label><input type="text" name="company_phone" value="<?= e($generalSettings['company_phone'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('company_email', 'Şirket E-Posta', 'Company Email')) ?></label><input type="text" name="company_email" value="<?= e($generalSettings['company_email'] ?? '') ?>"></div>
                        <div class="f"><label><?= e(ui_text('display_currency', 'Gösterim PB', 'Display Currency')) ?></label><select name="display_currency"><?php foreach ($SUPPORTED_CURRENCIES as $c): ?><option value="<?= e($c) ?>" <?= ($displayCur === $c ? 'selected' : '') ?>><?= e($c) ?></option><?php endforeach; ?></select></div>
                        <div class="f"><label><?= e(lang('language', 'Dil')) ?></label><select name="language"><?php foreach (web_lang_available_codes() as $langCodeOption): $langLabel = $langCodeOption; if ($langCodeOption === 'TR') { $langLabel = lang('turkish', 'Turkce'); } elseif ($langCodeOption === 'EN') { $langLabel = lang('english', 'English'); } ?><option value="<?= e($langCodeOption) ?>" <?= ($currentLangCode === $langCodeOption ? 'selected' : '') ?>><?= e($langLabel) ?></option><?php endforeach; ?></select></div>
                        <div class="f"><label><?= e(ui_text('theme', 'Tema', 'Theme')) ?></label><select name="theme_name"><?php foreach ($themeOptions as $t): ?><option value="<?= e($t) ?>" <?= ($currentThemeName === $t ? 'selected' : '') ?>><?= e($t) ?></option><?php endforeach; ?></select></div>
                        <div class="f">
                            <label>&nbsp;</label>
                            <button type="button" id="toggle-new-theme-btn" class="btn"><?= e(ui_text('create_new_theme', 'Yeni Tema Ekle', 'Add New Theme')) ?></button>
                        </div>
                        <?php if ($customThemeMap): ?>
                            <div class="f span-2"><label><?= e(ui_text('web_custom_themes', 'Özel Temalar', 'Custom Themes')) ?></label><div class="help"><?= e(implode(', ', $customNames)) ?></div></div>
                        <?php endif; ?>
                        <div id="new-theme-panel" class="fg compact-grid span-4 hidden" style="margin:0">
                            <div class="f span-2"><label><?= e(ui_text('theme_name', 'Yeni Tema Adı (opsiyonel)', 'New Theme Name (optional)')) ?></label><input type="text" name="new_theme_name" placeholder="<?= e(ui_text('web_theme_name_ph', 'Örn: Marka Gecesi', 'Example: Brand Night')) ?>"></div>
                            <div class="f"><label><?= e(ui_text('primary_color', 'Birincil Renk', 'Primary Color')) ?></label><input type="color" name="new_theme_primary" value="#285430"></div>
                            <div class="f"><label><?= e(ui_text('secondary_color', 'İkincil Renk', 'Secondary Color')) ?></label><input type="color" name="new_theme_secondary" value="#5F8D4E"></div>
                            <div class="f"><label><?= e(ui_text('background_color', 'Arka Plan', 'Background')) ?></label><input type="color" name="new_theme_background" value="#F1F8E9"></div>
                            <div class="f"><label><?= e(ui_text('text_color_dark', 'Yazı (Koyu)', 'Text (Dark)')) ?></label><input type="color" name="new_theme_text_dark" value="#000000"></div>
                            <div class="f"><label><?= e(ui_text('text_color_light', 'Yazı (Açık)', 'Text (Light)')) ?></label><input type="color" name="new_theme_text_light" value="#FFFFFF"></div>
                            <div class="f"><label><?= e(ui_text('danger_color', 'Tehlike', 'Danger')) ?></label><input type="color" name="new_theme_danger" value="#D32F2F"></div>
                            <div class="f"><label><?= e(ui_text('warning_color', 'Uyarı', 'Warning')) ?></label><input type="color" name="new_theme_warning" value="#FFC107"></div>
                        </div>
                        <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                    </form>
                    <script>
                        (function () {
                            var btn = document.getElementById('toggle-new-theme-btn');
                            var panel = document.getElementById('new-theme-panel');
                            if (!btn || !panel) return;
                            btn.addEventListener('click', function () {
                                var opening = panel.classList.contains('hidden');
                                panel.classList.toggle('hidden');
                                btn.textContent = opening ? '<?= e(ui_text('web_hide_new_theme', 'Yeni Tema Ekleme Alanını Gizle', 'Hide New Theme Area')) ?>' : '<?= e(ui_text('create_new_theme', 'Yeni Tema Ekle', 'Add New Theme')) ?>';
                            });
                        })();
                    </script>

                <?php elseif ($sub === 'settings-security'): ?>
                    <?php if (!$isAdmin): ?>
                        <div class="flash err"><?= e(ui_text('admin_only_area', 'Bu alan sadece admin kullanıcıya açıktır.', 'This area is only available to admin users.')) ?></div>
                    <?php endif; ?>
                    <div class="panel" style="margin:0 0 12px">
                        <h3><?= e(ui_text('profile_password', 'Profil Şifresi', 'Profile Password')) ?></h3>
                        <p class="muted"><?= e(ui_text('current_status', 'Mevcut durum', 'Current status')) ?>: <?= (($generalSettings['password_enabled'] ?? '0') === '1' ? ui_text('active', 'Aktif', 'Active') : ui_text('passive', 'Pasif', 'Passive')) ?>. <?= e(ui_text('web_profile_password_hint', 'Boş kayıt edilirse profil şifresi pasif olur ve şifre temizlenir.', 'If saved blank, the profile password is disabled and cleared.')) ?></p>
                        <form method="post" class="fg">
                            <input type="hidden" name="action" value="settings_security_save">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                            <div class="f"><label><?= e(ui_text('change_password', 'Yeni Profil Şifresi', 'New Profile Password')) ?></label><input type="password" name="new_admin_password" placeholder="<?= e(ui_text('web_fill_to_enable', 'Aktif etmek için doldurun', 'Fill to enable')) ?>"></div>
                            <div class="f"><label><?= e(ui_text('confirm_password_prompt', 'Yeni Profil Şifresi (Tekrar)', 'Repeat New Profile Password')) ?></label><input type="password" name="new_admin_password_repeat"></div>
                            <div class="f"><label>&nbsp;</label><button class="btnp" type="submit" <?= (!$isAdmin ? 'disabled' : '') ?>><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button></div>
                        </form>
                    </div>

                    <div class="panel" style="margin:0">
                        <h3><?= e(ui_text('web_change_customer_password', 'Müşteri Şifresini Değiştir', 'Change Customer Password')) ?></h3>
                        <form method="post" class="fg">
                            <input type="hidden" name="action" value="settings_customer_password_change">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                            <div class="f"><label><?= e(ui_text('web_current_customer_password', 'Mevcut Müşteri Şifresi', 'Current Customer Password')) ?></label><input type="password" name="current_customer_password" required></div>
                            <div class="f"><label><?= e(ui_text('web_new_customer_password', 'Yeni Müşteri Şifresi', 'New Customer Password')) ?></label><input type="password" name="new_customer_password" required></div>
                            <div class="f"><label><?= e(ui_text('web_new_customer_password_repeat', 'Yeni Müşteri Şifresi (Tekrar)', 'Repeat New Customer Password')) ?></label><input type="password" name="new_customer_password_repeat" required></div>
                            <div class="f"><label>&nbsp;</label><button class="btnp" type="submit" <?= (!$isAdmin ? 'disabled' : '') ?>><?= e(ui_text('web_change_customer_password', 'Müşteri Şifresini Değiştir', 'Change Customer Password')) ?></button></div>
                        </form>
                    </div>

                <?php elseif ($sub === 'settings-users'): ?>
                    <?php if (!$isAdmin): ?>
                        <div class="flash err"><?= e(ui_text('admin_only_area', 'Bu alan sadece admin kullanıcıya açıktır.', 'This area is only available to admin users.')) ?></div>
                    <?php endif; ?>
                    <div class="panel" style="margin:0 0 12px">
                        <h3><?= e(ui_text('web_add_user', 'Kullanıcı Ekle', 'Add User')) ?></h3>
                        <form method="post" class="fg">
                            <input type="hidden" name="action" value="settings_user_add">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                            <div class="f"><label><?= e(ui_text('username_label', 'Kullanıcı Adı', 'Username')) ?></label><input type="text" name="new_username" required></div>
                            <div class="f"><label><?= e(ui_text('full_name', 'Ad Soyad', 'Full Name')) ?></label><input type="text" name="new_full_name" required></div>
                            <div class="f"><label><?= e(ui_text('role', 'Rol', 'Role')) ?></label><select name="new_role"><option value="staff"><?= e(ui_text('role_employee', 'Çalışan', 'Staff')) ?></option><option value="admin"><?= e(ui_text('role_admin', 'Yönetici', 'Admin')) ?></option></select></div>
                            <div class="f"><label><?= e(ui_text('password', 'Şifre', 'Password')) ?></label><input type="password" name="new_password"></div>
                            <input type="hidden" name="new_permissions_raw" value="">
                            <div class="f span-2">
                                <label><?= e(ui_text('tabs_header', 'İzinler', 'Permissions')) ?></label>
                                <div class="fg compact-grid" style="margin:0">
                                    <?php foreach ($AVAILABLE_PERMISSION_OPTIONS as $permCode => $permLabel): ?>
                                        <div class="f"><label><input type="checkbox" name="new_permissions[]" value="<?= e($permCode) ?>"> <?= e(ui_text($permCode, $permLabel, $permCode)) ?> <span class="muted">(<?= e($permCode) ?>)</span></label></div>
                                    <?php endforeach; ?>
                                </div>
                            </div>
                            <div class="f span-2">
                                <label><?= e(ui_text('emp_depot_access', 'Depo Erişimi', 'Depot Access')) ?></label>
                                <div class="fg compact-grid" style="margin:0">
                                    <?php if (!$settingsDepotOptions): ?>
                                        <div class="f"><span class="muted"><?= e(ui_text('web_no_depot', 'Depo bulunmuyor.', 'No depots found.')) ?></span></div>
                                    <?php else: ?>
                                        <?php foreach ($settingsDepotOptions as $depOpt): ?>
                                            <div class="f"><label><input type="checkbox" name="new_scope_depots[]" value="<?= e($depOpt['id'] ?? '') ?>"> <?= e($depOpt['name'] ?? '') ?></label></div>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </div>
                            </div>
                            <div class="f span-2">
                                <label><?= e(ui_text('emp_cash_access', 'Kasa Erişimi', 'Cash Access')) ?></label>
                                <div class="fg compact-grid" style="margin:0">
                                    <?php if (!$settingsCashAccountOptions): ?>
                                        <div class="f"><span class="muted"><?= e(ui_text('web_no_cash_account', 'Kasa hesabı bulunmuyor.', 'No cash account found.')) ?></span></div>
                                    <?php else: ?>
                                        <?php foreach ($settingsCashAccountOptions as $accOpt): ?>
                                            <div class="f"><label><input type="checkbox" name="new_scope_cash_accounts[]" value="<?= e($accOpt['id'] ?? '') ?>"> <?= e($accOpt['name'] ?? '') ?></label></div>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </div>
                            </div>
                            <div class="f span-2">
                                <label><?= e(ui_text('emp_card_access', 'Kart Erişimi', 'Card Access')) ?></label>
                                <div class="fg compact-grid" style="margin:0">
                                    <?php if (!$settingsCardAccountOptions): ?>
                                        <div class="f"><span class="muted"><?= e(ui_text('web_no_card_account', 'Kart hesabı bulunmuyor.', 'No card account found.')) ?></span></div>
                                    <?php else: ?>
                                        <?php foreach ($settingsCardAccountOptions as $accOpt): ?>
                                            <div class="f"><label><input type="checkbox" name="new_scope_card_accounts[]" value="<?= e($accOpt['id'] ?? '') ?>"> <?= e($accOpt['name'] ?? '') ?></label></div>
                                        <?php endforeach; ?>
                                    <?php endif; ?>
                                </div>
                            </div>
                            <div class="f"><label>&nbsp;</label><button class="btnp" type="submit" <?= (!$isAdmin ? 'disabled' : '') ?>><?= e(ui_text('add', 'Ekle', 'Add')) ?></button></div>
                        </form>
                    </div>
                    <div class="tbl">
                        <table>
                            <thead><tr><th><?= e(ui_text('user', 'Kullanıcı', 'User')) ?></th><th><?= e(ui_text('full_name', 'Ad Soyad', 'Full Name')) ?></th><th><?= e(ui_text('role', 'Rol', 'Role')) ?></th><th><?= e(ui_text('status', 'Durum', 'Status')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$settingsUsers): ?>
                                <tr><td colspan="5" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($settingsUsers as $ru): ?>
                                    <tr>
                                        <td><?= e($ru['username'] ?? '') ?></td>
                                        <td><?= e($ru['full_name'] ?? '') ?></td>
                                        <td><?= e(role_label((string)($ru['role'] ?? ''))) ?></td>
                                        <td><?= ((int)($ru['is_active'] ?? 0) === 1 ? ui_text('active', 'Aktif', 'Active') : ui_text('passive', 'Pasif', 'Passive')) ?></td>
                                        <td>
                                            <form method="post" class="inline">
                                                <input type="hidden" name="action" value="settings_user_toggle">
                                                <input type="hidden" name="user_id" value="<?= e($ru['id'] ?? '') ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <label><input type="checkbox" name="is_active" value="1" <?= ((int)($ru['is_active'] ?? 0) === 1 ? 'checked' : '') ?>> <?= e(ui_text('active', 'Aktif', 'Active')) ?></label>
                                                <button class="btn" type="submit"><?= e(ui_text('update', 'Güncelle', 'Update')) ?></button>
                                            </form>
                                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_user', 'Kullanıcı silinsin mi?', 'Delete user?')) ?>');">
                                                <input type="hidden" name="action" value="settings_user_delete">
                                                <input type="hidden" name="user_id" value="<?= e($ru['id'] ?? '') ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button class="btnd" type="submit" <?= (!$isAdmin ? 'disabled' : '') ?>><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'settings-taxes'): ?>
                    <?php
                    $selectedTaxProfileUi = trim((string)($_GET['tax_profile_id'] ?? ''));
                    if ($selectedTaxProfileUi === '' && $taxProfileRows) {
                        $selectedTaxProfileUi = trim((string)($taxProfileRows[0]['id'] ?? ''));
                    }
                    $selectedTaxProfileNameUi = '';
                    $selectedTaxItemsMap = [];
                    foreach ($taxProfileRows as $tpRow) {
                        $tpId = trim((string)($tpRow['id'] ?? ''));
                        if ($tpId === $selectedTaxProfileUi) {
                            $selectedTaxProfileNameUi = trim((string)($tpRow['name'] ?? ''));
                            break;
                        }
                    }
                    if ($selectedTaxProfileUi !== '' && isset($taxProfileItemsMap[$selectedTaxProfileUi]) && is_array($taxProfileItemsMap[$selectedTaxProfileUi])) {
                        foreach ($taxProfileItemsMap[$selectedTaxProfileUi] as $itemRow) {
                            $txId = trim((string)($itemRow['tax_id'] ?? ''));
                            if ($txId !== '') {
                                $selectedTaxItemsMap[$txId] = $itemRow;
                            }
                        }
                    }
                    ?>
                    <div class="g2">
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('add_tax', 'Vergi Tanımı Ekle', 'Add Tax Definition')) ?></h3>
                            <form method="post" class="fg">
                                <input type="hidden" name="action" value="tax_definition_add">
                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                <div class="f"><label><?= e(ui_text('taxes', 'Vergi Adı', 'Tax Name')) ?></label><input type="text" name="tax_name" required></div>
                                <div class="f">
                                    <label><?= e(ui_text('type', 'Tip', 'Type')) ?></label>
                                    <select name="tax_type">
                                        <option value="percent"><?= e(ui_text('tax_type_percent', 'Yüzde', 'Percent')) ?></option>
                                        <option value="fixed"><?= e(ui_text('tax_type_fixed', 'Sabit', 'Fixed')) ?></option>
                                    </select>
                                </div>
                                <div class="f"><label><?= e(ui_text('tax_amount_col', 'Oran / Tutar', 'Rate / Amount')) ?></label><input type="text" name="tax_rate" placeholder="0,00" required></div>
                                <div class="f"><label><?= e(ui_text('country', 'Ülke (opsiyonel)', 'Country (optional)')) ?></label><input type="text" name="tax_country" placeholder="TR" maxlength="2"></div>
                                <div class="f"><label><?= e(ui_text('status', 'Durum', 'Status')) ?></label><label><input type="checkbox" name="tax_active" value="1" checked> <?= e(ui_text('active', 'Aktif', 'Active')) ?></label></div>
                                <div class="f"><label>&nbsp;</label><button class="btnp" type="submit"><?= e(ui_text('add', 'Ekle', 'Add')) ?></button></div>
                            </form>
                        </div>
                        <div class="panel" style="margin:0">
                            <h3><?= e(ui_text('tax_ledger', 'Vergi Defteri', 'Tax Ledger')) ?></h3>
                            <p class="muted"><?= e(ui_text('web_tax_hint', 'Web panelinde sadece vergi tanımları ve uygulama defteri gösterilir. Profil bazlı düzenleme arayüzü masaüstü uyumu için gizlendi.', 'The web panel shows only tax definitions and the application ledger. Profile-based editing UI stays hidden for desktop compatibility.')) ?></p>
                        </div>
                    </div>

                    <h3 style="margin-top:12px"><?= e(ui_text('taxes', 'Vergi Tanımları', 'Tax Definitions')) ?></h3>
                    <div class="tbl">
                        <table>
                            <thead><tr><th><?= e(ui_text('taxes', 'Vergi', 'Tax')) ?></th><th><?= e(ui_text('type', 'Tip', 'Type')) ?></th><th><?= e(ui_text('tax_amount_col', 'Oran/Tutar', 'Rate/Amount')) ?></th><th><?= e(ui_text('country', 'Ülke', 'Country')) ?></th><th><?= e(ui_text('status', 'Durum', 'Status')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$taxDefinitionRows): ?>
                                <tr><td colspan="6" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($taxDefinitionRows as $tx): ?>
                                    <?php
                                    $txId = trim((string)($tx['id'] ?? ''));
                                    if ($txId === '') {
                                        continue;
                                    }
                                    $txType = strtolower(trim((string)($tx['type'] ?? 'percent')));
                                    if (!in_array($txType, ['percent', 'fixed'], true)) {
                                        $txType = 'percent';
                                    }
                                    ?>
                                    <tr>
                                        <td><?= e($tx['name'] ?? '') ?></td>
                                        <td><?= e($txType === 'fixed' ? ui_text('tax_type_fixed', 'Sabit', 'Fixed') : ui_text('tax_type_percent', 'Yüzde', 'Percent')) ?></td>
                                        <td><?= e(number_format((float)($tx['rate'] ?? 0), 2, ',', '.')) ?></td>
                                        <td><?= e($tx['country'] ?? '-') ?></td>
                                        <td><?= ((int)($tx['active'] ?? 0) === 1 ? ui_text('active', 'Aktif', 'Active') : ui_text('passive', 'Pasif', 'Passive')) ?></td>
                                        <td>
                                            <form method="post" class="inline">
                                                <input type="hidden" name="action" value="tax_definition_update">
                                                <input type="hidden" name="tax_id" value="<?= e($txId) ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <input type="text" name="tax_name" value="<?= e($tx['name'] ?? '') ?>" required style="max-width:140px">
                                                <select name="tax_type" style="max-width:110px">
                                                    <option value="percent" <?= ($txType === 'percent' ? 'selected' : '') ?>><?= e(ui_text('tax_type_percent', 'Yüzde', 'Percent')) ?></option>
                                                    <option value="fixed" <?= ($txType === 'fixed' ? 'selected' : '') ?>><?= e(ui_text('tax_type_fixed', 'Sabit', 'Fixed')) ?></option>
                                                </select>
                                                <input type="text" name="tax_rate" value="<?= e(number_format((float)($tx['rate'] ?? 0), 2, '.', '')) ?>" style="max-width:90px">
                                                <input type="text" name="tax_country" value="<?= e($tx['country'] ?? '') ?>" maxlength="2" style="max-width:60px">
                                                <label><input type="checkbox" name="tax_active" value="1" <?= ((int)($tx['active'] ?? 0) === 1 ? 'checked' : '') ?>> <?= e(ui_text('active', 'Aktif', 'Active')) ?></label>
                                                <button class="btn" type="submit"><?= e(ui_text('save', 'Kaydet', 'Save')) ?></button>
                                            </form>
                                            <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_delete_tax', 'Vergi tanımı silinsin mi?', 'Delete tax definition?')) ?>');">
                                                <input type="hidden" name="action" value="tax_definition_delete">
                                                <input type="hidden" name="tax_id" value="<?= e($txId) ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button class="btnd" type="submit"><?= e(ui_text('delete', 'Sil', 'Delete')) ?></button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                    <?php if (false): ?>
                    <h3 style="margin-top:12px">Vergi Profilleri</h3>
                    <div class="tbl">
                        <table>
                            <thead><tr><th>Profil</th><th>Aciklama</th><th>Kalem</th><th>Islem</th></tr></thead>
                            <tbody>
                            <?php if (!$taxProfileRows): ?>
                                <tr><td colspan="4" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($taxProfileRows as $tp): ?>
                                    <?php
                                    $tpId = trim((string)($tp['id'] ?? ''));
                                    if ($tpId === '') {
                                        continue;
                                    }
                                    $itemCount = count($taxProfileItemsMap[$tpId] ?? []);
                                    ?>
                                    <tr>
                                        <td><?= e($tp['name'] ?? '') ?></td>
                                        <td><?= e($tp['description'] ?? '') ?></td>
                                        <td><?= e((string)$itemCount) ?></td>
                                        <td>
                                            <form method="post" class="inline">
                                                <input type="hidden" name="action" value="tax_profile_update">
                                                <input type="hidden" name="tax_profile_id" value="<?= e($tpId) ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <input type="text" name="tax_profile_name" value="<?= e($tp['name'] ?? '') ?>" required style="max-width:140px">
                                                <input type="text" name="tax_profile_description" value="<?= e($tp['description'] ?? '') ?>" style="max-width:220px">
                                                <button class="btn" type="submit">Kaydet</button>
                                            </form>
                                            <form method="post" class="inline" onsubmit="return confirm('Vergi profili silinsin mi?');">
                                                <input type="hidden" name="action" value="tax_profile_delete">
                                                <input type="hidden" name="tax_profile_id" value="<?= e($tpId) ?>">
                                                <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                <button class="btnd" type="submit">Sil</button>
                                            </form>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                    <div class="panel" style="margin:12px 0">
                        <h3>Profil Vergi Kalemleri</h3>
                        <?php if (!$taxProfileRows): ?>
                            <p class="muted">Once vergi profili olusturun.</p>
                        <?php elseif (!$taxDefinitionRows): ?>
                            <p class="muted">Once vergi tanimi olusturun.</p>
                        <?php else: ?>
                            <form method="get" class="row" style="margin-bottom:8px">
                                <input type="hidden" name="cat" value="<?= e($cat) ?>">
                                <input type="hidden" name="sub" value="<?= e($sub) ?>">
                                <label>Profil</label>
                                <select name="tax_profile_id" style="max-width:280px">
                                    <?php foreach ($taxProfileRows as $tp): ?>
                                        <?php $tpId = trim((string)($tp['id'] ?? '')); if ($tpId === '') { continue; } ?>
                                        <option value="<?= e($tpId) ?>" <?= ($selectedTaxProfileUi === $tpId ? 'selected' : '') ?>><?= e($tp['name'] ?? '') ?></option>
                                    <?php endforeach; ?>
                                </select>
                                <button class="btn" type="submit">Yukle</button>
                            </form>
                            <?php if ($selectedTaxProfileUi !== ''): ?>
                                <form method="post" class="fg">
                                    <input type="hidden" name="action" value="tax_profile_items_save">
                                    <input type="hidden" name="tax_profile_id" value="<?= e($selectedTaxProfileUi) ?>">
                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                    <div class="f span-4">
                                        <label>Secili Profil: <strong><?= e($selectedTaxProfileNameUi !== '' ? $selectedTaxProfileNameUi : $selectedTaxProfileUi) ?></strong></label>
                                        <div class="tbl">
                                            <table>
                                                <thead><tr><th>Sec</th><th>Vergi</th><th>Tip</th><th>Oran/Tutar</th><th>Oncelik</th><th>Varsayilan</th></tr></thead>
                                                <tbody>
                                                <?php foreach ($taxDefinitionRows as $tx): ?>
                                                    <?php
                                                    $txId = trim((string)($tx['id'] ?? ''));
                                                    if ($txId === '') {
                                                        continue;
                                                    }
                                                    $txType = strtolower(trim((string)($tx['type'] ?? 'percent')));
                                                    if (!in_array($txType, ['percent', 'fixed'], true)) {
                                                        $txType = 'percent';
                                                    }
                                                    $itemRow = $selectedTaxItemsMap[$txId] ?? null;
                                                    $isChecked = is_array($itemRow);
                                                    $priorityVal = (int)($itemRow['priority'] ?? 0);
                                                    $enabledVal = (int)($itemRow['enabled_by_default'] ?? 0);
                                                    ?>
                                                    <tr>
                                                        <td><input type="checkbox" name="profile_tax_id[]" value="<?= e($txId) ?>" <?= ($isChecked ? 'checked' : '') ?>></td>
                                                        <td><?= e($tx['name'] ?? '') ?></td>
                                                        <td><?= e($txType === 'fixed' ? 'Sabit' : 'Yuzde') ?></td>
                                                        <td><?= e(number_format((float)($tx['rate'] ?? 0), 2, ',', '.')) ?></td>
                                                        <td><input type="number" name="profile_tax_priority[<?= e($txId) ?>]" value="<?= e((string)$priorityVal) ?>" style="max-width:90px"></td>
                                                        <td><input type="checkbox" name="profile_tax_enabled[<?= e($txId) ?>]" value="1" <?= ($enabledVal === 1 ? 'checked' : '') ?>></td>
                                                    </tr>
                                                <?php endforeach; ?>
                                                </tbody>
                                            </table>
                                        </div>
                                    </div>
                                    <div class="f"><label>&nbsp;</label><button class="btnp" type="submit">Profil Kalemlerini Kaydet</button></div>
                                </form>
                            <?php endif; ?>
                        <?php endif; ?>
                    </div>
                    <?php endif; ?>

                    <h3 style="margin-top:12px"><?= e(ui_text('web_tax_applications_recent', 'Vergi Uygulama Kayıtları (Son 100)', 'Tax Application Records (Last 100)')) ?></h3>
                    <div class="tbl">
                        <table>
                            <thead><tr><th><?= e(ui_text('report_header_date', 'Tarih', 'Date')) ?></th><th><?= e(ui_text('report_header_source', 'Kaynak', 'Source')) ?></th><th><?= e(ui_text('subtotal', 'Ara Toplam', 'Subtotal')) ?></th><th><?= e(ui_text('report_header_tax', 'Vergi', 'Tax')) ?></th><th><?= e(ui_text('report_header_grand_total', 'Genel Toplam', 'Grand Total')) ?></th><th><?= e(ui_text('report_header_currency', 'PB', 'Currency')) ?></th><th><?= e(ui_text('taxes', 'Vergiler', 'Taxes')) ?></th></tr></thead>
                            <tbody>
                            <?php if (!$taxLedgerRows): ?>
                                <tr><td colspan="7" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                            <?php else: ?>
                                <?php foreach ($taxLedgerRows as $tl): ?>
                                    <?php
                                    $createdText = trim((string)($tl['created_at'] ?? ''));
                                    if ($createdText !== '') {
                                        $tsCreated = strtotime($createdText);
                                        if ($tsCreated !== false) {
                                            $createdText = date('d.m.Y H:i', $tsCreated);
                                        }
                                    }
                                    $srcTable = trim((string)($tl['source_table'] ?? ''));
                                    $srcId = trim((string)($tl['source_id'] ?? ''));
                                    $summaryText = '-';
                                    $snapRaw = trim((string)($tl['snapshot_json'] ?? ''));
                                    if ($snapRaw !== '') {
                                        $snap = json_decode($snapRaw, true);
                                        if (is_array($snap)) {
                                            $summaryText = trim((string)($snap['applied_taxes_summary'] ?? ''));
                                            if ($summaryText === '') {
                                                $names = [];
                                                $snapTaxes = $snap['taxes'] ?? [];
                                                if (is_array($snapTaxes)) {
                                                    foreach ($snapTaxes as $snapTax) {
                                                        if (!is_array($snapTax)) {
                                                            continue;
                                                        }
                                                        $taxNm = trim((string)($snapTax['name'] ?? ''));
                                                        if ($taxNm !== '') {
                                                            $names[] = $taxNm;
                                                        }
                                                    }
                                                }
                                                $summaryText = $names ? implode(', ', $names) : '-';
                                            }
                                        }
                                    }
                                    ?>
                                    <tr>
                                        <td><?= e($createdText !== '' ? $createdText : '-') ?></td>
                                        <td><?= e(($srcTable !== '' ? $srcTable : '-') . ($srcId !== '' ? (' #' . $srcId) : '')) ?></td>
                                        <td><?= e(number_format((float)($tl['subtotal'] ?? 0), 2, ',', '.')) ?></td>
                                        <td><?= e(number_format((float)($tl['tax_total'] ?? 0), 2, ',', '.')) ?></td>
                                        <td><?= e(number_format((float)($tl['grand_total'] ?? 0), 2, ',', '.')) ?></td>
                                        <td><?= e($tl['currency'] ?? 'TRY') ?></td>
                                        <td><?= e($summaryText) ?></td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                            </tbody>
                        </table>
                    </div>

                <?php elseif ($sub === 'settings-transfer'): ?>
                    <?php if (!$isAdmin): ?>
                        <div class="flash err"><?= e(ui_text('admin_only_area', 'Bu alan sadece admin kullanıcıya açıktır.', 'This area is available to admin users only.')) ?></div>
                    <?php endif; ?>
                    <div class="panel" style="margin:0">
                        <h3><?= e(ui_text('cross_profile_transfer', 'Profiller Arası Veri Aktarımı', 'Cross-Profile Data Transfer')) ?></h3>
                        <form method="post" class="fg">
                            <input type="hidden" name="action" value="settings_transfer_data">
                            <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                            <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                            <div class="f">
                                <label><?= e(ui_text('target_profile', 'Hedef Profil', 'Target Profile')) ?></label>
                                <select name="target_profile_id" required>
                                    <option value=""><?= e(ui_text('select', 'Seçin...', 'Select...')) ?></option>
                                    <?php foreach ($profileOptionsAll as $po): ?>
                                        <?php if ((string)($po['id'] ?? '') === (string)($_SESSION['profile']['id'] ?? '')) continue; ?>
                                        <option value="<?= e($po['id'] ?? '') ?>"><?= e($po['name'] ?? '') ?></option>
                                    <?php endforeach; ?>
                                </select>
                            </div>
                            <div class="f span-4">
                                <label><?= e(ui_text('transfer_sections', 'Aktarım Bölümleri', 'Transfer Sections')) ?></label>
                                <div class="row">
                                    <label><input type="checkbox" name="transfer_sections[]" value="persons"> <?= e(ui_text('customers', 'Müşteriler', 'Customers')) ?></label>
                                    <label><input type="checkbox" name="transfer_sections[]" value="depots"> <?= e(ui_text('depots', 'Depolar', 'Depots')) ?></label>
                                    <label><input type="checkbox" name="transfer_sections[]" value="products"> <?= e(ui_text('product_definitions', 'Ürün Tanımları', 'Product Definitions')) ?></label>
                                    <label><input type="checkbox" name="transfer_sections[]" value="accounts"> <?= e(ui_text('cash_card_accounts', 'Kasa/Kart Hesapları', 'Cash/Card Accounts')) ?></label>
                                    <label><input type="checkbox" name="transfer_sections[]" value="services"> <?= e(ui_text('services', 'Hizmetler', 'Services')) ?></label>
                                    <label><input type="checkbox" name="transfer_sections[]" value="repairs"> <?= e(ui_text('repair_records', 'Tamir Kayıtları', 'Repair Records')) ?></label>
                                    <label><input type="checkbox" name="transfer_sections[]" value="agenda"> <?= e(ui_text('agenda_tab', 'Ajanda', 'Agenda')) ?></label>
                                    <label><input type="checkbox" name="transfer_sections[]" value="settings"> <?= e(ui_text('general_settings', 'Genel Ayarlar', 'General Settings')) ?></label>
                                </div>
                            </div>
                            <div class="f"><label>&nbsp;</label><button class="btnp" type="submit" <?= (!$isAdmin ? 'disabled' : '') ?> onclick="return confirm('<?= e(ui_text('confirm_transfer_selected_data', 'Seçili veriler hedef profile aktarılsın mı?', 'Transfer the selected data to the target profile?')) ?>');"><?= e(ui_text('start_transfer', 'Aktarımı Başlat', 'Start Transfer')) ?></button></div>
                        </form>
                    </div>

                <?php elseif ($sub === 'settings-devices'): ?>
                    <?php
                    $settingsActiveDeviceRows = array_values(array_filter($settingsDeviceRows, static function (array $row): bool {
                        return (int)($row['is_blocked'] ?? 0) === 0 && (int)($row['is_active_device'] ?? 0) === 1;
                    }));
                    $settingsActiveWebSessionRows = array_values(array_filter($settingsWebSessionRows, static function (array $row): bool {
                        return (int)($row['is_active_now'] ?? 0) === 1;
                    }));
                    ?>
                    <div class="panel" style="margin:0 0 12px">
                        <h3><?= e(ui_text('active_devices', 'Aktif Cihazlar', 'Active Devices')) ?></h3>
                        <div class="tbl">
                            <table>
                                <thead>
                                <tr><th><?= e(ui_text('group', 'Grup', 'Group')) ?></th><th><?= e(ui_text('device_name', 'Cihaz Adı', 'Device Name')) ?></th><th>IP</th><th><?= e(ui_text('last_seen', 'Son Görülme', 'Last Seen')) ?></th><th><?= e(ui_text('active_session', 'Aktif Oturum', 'Active Session')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr>
                                </thead>
                                <tbody>
                                <?php if (!$settingsActiveDeviceRows): ?>
                                    <tr><td colspan="6" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                                <?php else: ?>
                                    <?php foreach ($settingsActiveDeviceRows as $dv): ?>
                                        <?php
                                        $dvId = (int)($dv['id'] ?? 0);
                                        if ($dvId <= 0) {
                                            continue;
                                        }
                                        ?>
                                        <tr>
                                            <td><?= e($dv['device_group'] ?? '-') ?></td>
                                            <td><?= e($dv['device_name'] ?? '-') ?></td>
                                            <td><?= e($dv['ip_address'] ?? '-') ?></td>
                                            <td><?= e($dv['last_seen_at'] ?? '-') ?></td>
                                            <td><?= e((string)((int)($dv['active_sessions'] ?? 0))) ?></td>
                                            <td>
                                                <form method="post" class="inline">
                                                    <input type="hidden" name="action" value="settings_device_rename">
                                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                    <input type="hidden" name="device_id" value="<?= e((string)$dvId) ?>">
                                                    <input type="text" name="device_name" value="<?= e($dv['device_name'] ?? '') ?>" style="max-width:180px" required>
                                                    <button class="btn" type="submit"><?= e(ui_text('rename', 'Adı Değiştir', 'Rename')) ?></button>
                                                </form>
                                                <form method="post" class="inline">
                                                    <input type="hidden" name="action" value="settings_device_logout">
                                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                    <input type="hidden" name="device_id" value="<?= e((string)$dvId) ?>">
                                                    <button class="btn" type="submit"><?= e(ui_text('close_session', 'Oturumu Kapat', 'Close Session')) ?></button>
                                                </form>
                                                <form method="post" class="inline" onsubmit="return confirm('<?= e(ui_text('confirm_remove_device', 'Cihaz listeden kaldırılsın mı?', 'Remove device from the list?')) ?>');">
                                                    <input type="hidden" name="action" value="settings_device_remove">
                                                    <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                    <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                    <input type="hidden" name="device_id" value="<?= e((string)$dvId) ?>">
                                                    <button class="btnd" type="submit"><?= e(ui_text('remove', 'Kaldır', 'Remove')) ?></button>
                                                </form>
                                            </td>
                                        </tr>
                                    <?php endforeach; ?>
                                <?php endif; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>

                    <div class="panel" style="margin:0">
                        <h3><?= e(ui_text('active_web_sessions', 'Aktif Web Oturumları', 'Active Web Sessions')) ?></h3>
                        <div class="tbl">
                            <table>
                                <thead>
                                <tr><th><?= e(ui_text('device', 'Cihaz', 'Device')) ?></th><th>IP</th><th><?= e(ui_text('login', 'Giriş', 'Login')) ?></th><th><?= e(ui_text('last_active', 'Son Aktif', 'Last Active')) ?></th><th><?= e(ui_text('status', 'Durum', 'Status')) ?></th><th><?= e(ui_text('action', 'İşlem', 'Action')) ?></th></tr>
                                </thead>
                                <tbody>
                                <?php if (!$settingsActiveWebSessionRows): ?>
                                    <tr><td colspan="6" class="muted"><?= e(ui_text('no_records', 'Kayıt yok.', 'No records.')) ?></td></tr>
                                <?php else: ?>
                                    <?php foreach ($settingsActiveWebSessionRows as $ws): ?>
                                        <?php
                                        $sessionUuid = trim((string)($ws['session_uuid'] ?? ''));
                                        if ($sessionUuid === '') {
                                            continue;
                                        }
                                        $isClosed = (int)($ws['is_active_now'] ?? 0) !== 1;
                                        ?>
                                        <tr>
                                            <td><?= e($ws['device_name'] ?? '-') ?></td>
                                            <td><?= e($ws['ip_address'] ?? '-') ?></td>
                                            <td><?= e($ws['login_at'] ?? '-') ?></td>
                                            <td><?= e($ws['last_seen_at'] ?? '-') ?></td>
                                            <td><?= e($isClosed ? ui_text('closed', 'Kapalı', 'Closed') : ui_text('active', 'Aktif', 'Active')) ?></td>
                                            <td>
                                                <?php if (!$isClosed): ?>
                                                    <form method="post" class="inline">
                                                        <input type="hidden" name="action" value="settings_web_session_close">
                                                        <input type="hidden" name="return_cat" value="<?= e($cat) ?>">
                                                        <input type="hidden" name="return_sub" value="<?= e($sub) ?>">
                                                        <input type="hidden" name="session_uuid" value="<?= e($sessionUuid) ?>">
                                                        <button class="btnd" type="submit"><?= e(ui_text('close', 'Kapat', 'Close')) ?></button>
                                                    </form>
                                                <?php else: ?>
                                                    <span class="muted">-</span>
                                                <?php endif; ?>
                                            </td>
                                        </tr>
                                    <?php endforeach; ?>
                                <?php endif; ?>
                                </tbody>
                            </table>
                        </div>
                    </div>

                <?php endif; ?>
            </section>
        <?php endif; ?>
    <?php endif; ?>

</div>
<?php
if (!isset($webSessionClient)) { $webSessionClient = null; }
// Heartbeat/ping tabanli eski web-oturum istemci akisı devre dışı.
?>
<dialog id="global-scanner-modal" style="padding: 15px; text-align: center; border-radius: 18px; width: 90vw; max-width: 400px; background: var(--card);">
    <h3 style="margin-top:0; color: var(--primary);"><?= e(ui_text('scan_barcode', 'Barkod Okut', 'Scan Barcode')) ?></h3>
    <div id="global-reader" style="width: 100%; min-height: 250px; background: #000; border-radius: 12px; overflow: hidden; margin-bottom: 15px;"></div>
    <button type="button" class="btnd" id="global-scanner-close" style="width:100%; height:45px; font-size:16px;"><?= e(ui_text('close', 'Kapat', 'Close')) ?></button>
</dialog>

<script src="https://unpkg.com/html5-qrcode"></script>
<script>
window.GOLDER_WEB_SESSION = <?= json_encode($webSessionClient, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
</script>
<script src="assets/js/app.js?v=1.20.0"></script>
</body>
</html>


