<?php
declare(strict_types=1);

require_once __DIR__ . '/../app/init.php';
app_require_login();

$slug = app_normalize_whitespace((string) ($_GET['module'] ?? ''));
if ($slug === '') {
    app_redirect('index.php');
}

$requestedCompanyId = (int) ($_REQUEST['company_id'] ?? 0);
if ($slug === 'report_templates') {
    $query = $requestedCompanyId > 0 ? ('?company_id=' . urlencode((string) $requestedCompanyId)) : '';
    app_redirect('reports.php' . $query);
}

try {
    $definition = app_module_assert_access($slug);
} catch (Throwable $e) {
    app_flash('error', $e->getMessage());
    app_redirect('index.php');
}

$scopeRequired = app_module_scope_required($slug);
$companyId = app_effective_company_id($requestedCompanyId > 0 ? $requestedCompanyId : null, $scopeRequired);
if ($companyId !== null && $companyId > 0) {
    app_set_context_company_id($companyId);
}

$companies = app_current_user_company_options();
$moduleUrl = static function (array $extra = []) use ($slug, $companyId): string {
    $params = array_merge(['module' => $slug], $extra);
    if ($companyId !== null && $companyId > 0) {
        $params['company_id'] = $companyId;
    }
    return 'module.php?' . http_build_query($params);
};
$canCreate = app_current_role_can($slug, 'create');
$canEdit = app_current_role_can($slug, 'edit');
$canDelete = app_current_role_can($slug, 'delete');
$canImport = app_current_role_can($slug, 'import');
$canPost = app_current_role_can($slug, 'post');
if (in_array($slug, ['pos_orders', 'pos_sessions', 'pos_cash_movements'], true)) {
    $canDelete = false;
    $canImport = false;
}
$bulkDeleteEnabled = $canDelete && !in_array($slug, ['depreciation_runs', 'journal_entries', 'pos_orders', 'pos_sessions', 'pos_cash_movements'], true);
$bulkStatementGroupEnabled = $slug === 'chart_of_accounts' && $canEdit && $companyId !== null && $companyId > 0;
$bulkSelectionEnabled = $bulkDeleteEnabled || $bulkStatementGroupEnabled;

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $redirectAfterPost = $moduleUrl();
    try {
        app_verify_csrf();
        $action = (string) ($_POST['action'] ?? 'save');

        if ($action === 'save_role_permissions_matrix') {
            if ($slug !== 'role_permissions') {
                throw new RuntimeException('Invalid role permission action context.');
            }
            app_enforce_current_role_can('role_permissions', 'edit');
            $roleCode = app_current_role_normalize((string) ($_POST['role_code'] ?? ''));
            $allowedModules = (array) ($_POST['allowed_modules'] ?? []);
            $canEditAll = !empty($_POST['can_edit_all']);
            $canPostAll = !empty($_POST['can_post_all']);
            app_save_simple_role_permissions($roleCode, $allowedModules, $canEditAll, $canPostAll);
            $redirectAfterPost = $moduleUrl(['role_code' => $roleCode]);
            app_flash('success', 'Role permissions saved successfully.');
        } elseif ($action === 'save') {
            if ($slug === 'depreciation_runs') {
                throw new RuntimeException('Automatic entries are generated from depreciation run action only.');
            }
            $recordId = (int) ($_POST['id'] ?? 0);
            app_enforce_current_role_can($slug, $recordId > 0 ? 'edit' : 'create');
            $payload = $_POST;
            $userCompanyAccessIds = [];

            if (in_array($slug, ['payments', 'student_receipts', 'student_refunds'], true)) {
                app_enforce_current_role_can($slug, 'post', 'You do not have permission to post payments or vouchers in this module.');
            }
            $submittedStatus = strtolower(app_normalize_whitespace((string) ($payload['status'] ?? '')));
            if (app_module_requires_auto_posting($slug) && $submittedStatus !== '' && !in_array($submittedStatus, ['draft', 'cancelled'], true)) {
                app_enforce_current_role_can($slug, 'post', 'You do not have permission to post or confirm this document.');
            }

            if ($slug === 'companies' && isset($_FILES['logo_file']) && (int) ($_FILES['logo_file']['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK) {
                $payload['logo_path'] = app_store_uploaded_file($_FILES['logo_file'], 'companies', ['png', 'jpg', 'jpeg', 'webp', 'svg']);
            }

            if ($slug === 'users' && $recordId > 0 && $recordId === (int) (app_current_user()['id'] ?? 0) && !app_is_admin()) {
                throw new RuntimeException('You cannot edit your own user from this module.');
            }

            if ($slug === 'users') {
                $userCompanyAccessIds = array_map('intval', (array) ($_POST['company_access_ids'] ?? []));
                $primaryCompanyId = app_integer($payload['company_id'] ?? 0);
                if ($primaryCompanyId <= 0 && $userCompanyAccessIds) {
                    $primaryCompanyId = (int) $userCompanyAccessIds[0];
                    $payload['company_id'] = (string) $primaryCompanyId;
                }
                if ($primaryCompanyId > 0) {
                    $userCompanyAccessIds[] = $primaryCompanyId;
                }
            }

            if ($slug === 'journal_entries') {
                if ($companyId === null || $companyId <= 0) {
                    throw new RuntimeException('Company context is required for journal entries.');
                }
                $savedId = app_save_manual_journal_entry((int) $companyId, $payload, $recordId > 0 ? $recordId : null);
                $redirectAfterPost = $moduleUrl(['edit' => $savedId]);
                app_flash('success', 'Journal entry saved successfully.');
            } else {
                $savedId = app_module_save($slug, $payload, $companyId, $recordId > 0 ? $recordId : null);
                app_flash('success', 'Record saved successfully.');
            }
            if ($slug === 'users') {
                app_sync_user_company_access($savedId, $userCompanyAccessIds);
            }
        } elseif ($action === 'delete') {
            app_enforce_current_role_can($slug, 'delete');
            if ($slug === 'depreciation_runs') {
                $recordIdForCheck = (int) ($_POST['id'] ?? 0);
                $runForCheck = app_module_find($slug, $recordIdForCheck, $companyId);
                $runStatus = strtolower(app_normalize_whitespace((string) ($runForCheck['status'] ?? 'draft')));
                if ($runStatus === 'posted') {
                    throw new RuntimeException('Reset or cancel the depreciation run before deleting it.');
                }
            }
            $recordId = (int) ($_POST['id'] ?? 0);
            if ($slug === 'users' && $recordId === (int) (app_current_user()['id'] ?? 0)) {
                throw new RuntimeException(app_t('You cannot delete the current logged-in user.'));
            }

            app_module_delete($slug, $recordId, $companyId);
            if ($slug === 'users') {
                app_delete_user_company_access($recordId);
            }
            app_flash('success', 'Record deleted successfully.');
        } elseif ($action === 'bulk_delete') {
            if (!$bulkDeleteEnabled) {
                throw new RuntimeException('Bulk delete is not available for this module.');
            }
            app_enforce_current_role_can($slug, 'delete');

            $recordIds = array_values(array_unique(array_filter(
                array_map('intval', (array) ($_POST['record_ids'] ?? [])),
                static fn (int $id): bool => $id > 0
            )));
            if (!$recordIds) {
                throw new RuntimeException(app_t('No records selected.'));
            }
            if ($slug === 'users' && in_array((int) (app_current_user()['id'] ?? 0), $recordIds, true)) {
                throw new RuntimeException(app_t('You cannot delete the current logged-in user.'));
            }

            $pdo = app_pdo();
            $startedTransaction = false;
            try {
                if (!$pdo->inTransaction()) {
                    $pdo->beginTransaction();
                    $startedTransaction = true;
                }

                foreach ($recordIds as $recordId) {
                    app_module_delete($slug, $recordId, $companyId);
                    if ($slug === 'users') {
                        app_delete_user_company_access($recordId);
                    }
                }

                if ($startedTransaction && $pdo->inTransaction()) {
                    $pdo->commit();
                }
            } catch (Throwable $e) {
                if ($startedTransaction && $pdo->inTransaction()) {
                    $pdo->rollBack();
                }
                throw $e;
            }

            app_flash('success', count($recordIds) . ' record(s) deleted successfully.');
        } elseif ($action === 'bulk_assign_statement_group') {
            if (!$bulkStatementGroupEnabled) {
                throw new RuntimeException('Bulk statement group assignment is not available for this module.');
            }
            app_enforce_current_role_can('chart_of_accounts', 'edit');

            $recordIds = array_values(array_unique(array_filter(
                array_map('intval', (array) ($_POST['record_ids'] ?? [])),
                static fn (int $id): bool => $id > 0
            )));
            if (!$recordIds) {
                throw new RuntimeException(app_t('No records selected.'));
            }

            $statementGroupCode = app_normalize_whitespace((string) ($_POST['financial_statement_group_code'] ?? ''));
            if ($statementGroupCode === '') {
                throw new RuntimeException('Choose a financial statement group first.');
            }
            $statementGroup = app_financial_statement_group_by_code((int) $companyId, $statementGroupCode);
            if (!$statementGroup) {
                throw new RuntimeException('Select a valid financial statement group.');
            }
            $mainSection = app_normalize_whitespace((string) ($statementGroup['main_section'] ?? ''));
            $section = app_financial_statement_main_sections()[$mainSection] ?? null;
            $allowedTypes = array_map('strtolower', (array) ($section['account_types'] ?? []));
            if (!$section || !$allowedTypes) {
                throw new RuntimeException('Selected financial statement group is not configured correctly.');
            }

            $pdo = app_pdo();
            $placeholders = implode(',', array_fill(0, count($recordIds), '?'));
            $stmt = $pdo->prepare(
                'SELECT id, account_code, account_type
                 FROM chart_of_accounts
                 WHERE company_id = ?
                   AND id IN (' . $placeholders . ')'
            );
            $stmt->execute(array_merge([(int) $companyId], $recordIds));
            $accounts = $stmt->fetchAll() ?: [];
            if (count($accounts) !== count($recordIds)) {
                throw new RuntimeException('One or more selected accounts were not found.');
            }
            foreach ($accounts as $account) {
                $accountType = strtolower(app_normalize_whitespace((string) ($account['account_type'] ?? '')));
                if (!in_array($accountType, $allowedTypes, true)) {
                    throw new RuntimeException('Account ' . (string) ($account['account_code'] ?? '') . ' is not compatible with the selected financial statement group.');
                }
            }

            $updateStmt = $pdo->prepare(
                'UPDATE chart_of_accounts
                 SET financial_statement_group_code = :group_code
                 WHERE company_id = :company_id
                   AND id = :id'
            );
            foreach ($recordIds as $recordId) {
                $updateStmt->execute([
                    'group_code' => $statementGroupCode,
                    'company_id' => (int) $companyId,
                    'id' => $recordId,
                ]);
            }

            app_flash('success', count($recordIds) . ' account(s) assigned to financial statement group successfully.');
        } elseif ($action === 'import') {
            app_enforce_current_role_can($slug, 'import');
            if (in_array($slug, ['depreciation_runs', 'prepaid_amortization_runs', 'ifrs16_lease_schedules', 'pos_orders', 'pos_sessions', 'pos_cash_movements'], true)) {
                throw new RuntimeException('Import is disabled for automatic entry generation modules.');
            }
            if (!isset($_FILES['import_file'])) {
                throw new RuntimeException('Please choose a file first.');
            }

            $relativeImportPath = app_store_uploaded_file($_FILES['import_file'], 'imports', ['csv', 'txt', 'xlsx']);
            $absoluteImportPath = app_public_path($relativeImportPath);
            $result = app_module_import_from_file($slug, $absoluteImportPath, $companyId);
            $message = sprintf(
                'Import completed: %d inserted, %d skipped, %d errors.',
                (int) ($result['inserted'] ?? 0),
                (int) ($result['skipped'] ?? 0),
                count((array) ($result['errors'] ?? []))
            );
            app_flash('success', $message);

            $errors = (array) ($result['errors'] ?? []);
            if ($errors) {
                app_flash('error', implode(' | ', array_slice($errors, 0, 4)));
            }
        } elseif ($action === 'run_depreciation') {
            if ($slug !== 'depreciation_runs') {
                throw new RuntimeException('Invalid depreciation run action context.');
            }
            app_enforce_current_role_can($slug, 'create');
            if ($companyId === null || $companyId <= 0) {
                throw new RuntimeException('Company context is required to run depreciation.');
            }

            $runResult = app_run_depreciation_cycle((int) $companyId, $_POST);
            $runNumber = app_normalize_whitespace((string) ($runResult['run_number'] ?? ''));
            $runsCount = (int) ($runResult['runs_count'] ?? 0);
            $skippedCount = (int) ($runResult['skipped_count'] ?? 0);
            $assetsCount = (int) ($runResult['assets_count'] ?? 0);
            $totalAmount = app_money_format($runResult['total_amount'] ?? 0);
            app_flash(
                'success',
                'Depreciation created as draft. Runs: ' . number_format($runsCount) . ', Skipped: ' . number_format($skippedCount) . ', Assets: ' . number_format($assetsCount) . ', Amount: ' . $totalAmount
            );
            $redirectAfterPost = $moduleUrl(['edit' => (int) ($runResult['run_id'] ?? 0)]);
        } elseif ($action === 'generate_ifrs16_schedule') {
            if ($slug !== 'ifrs16_leases') {
                throw new RuntimeException('Invalid IFRS16 generation action context.');
            }
            app_enforce_current_role_can($slug, 'create');
            if ($companyId === null || $companyId <= 0) {
                throw new RuntimeException('Company context is required to generate IFRS16 entries.');
            }
            $leaseId = (int) ($_POST['id'] ?? 0);
            if ($leaseId <= 0) {
                throw new RuntimeException('Save the lease before generating IFRS16 entries.');
            }

            $result = app_generate_ifrs16_lease_schedule((int) $companyId, $leaseId);
            app_flash(
                'success',
                'IFRS16 draft entries generated. Schedule lines: ' . number_format((int) ($result['schedules_count'] ?? 0)) . ', PV Liability: ' . app_money_format($result['pv_lease_liability'] ?? 0) . ', ROU Asset: ' . app_money_format($result['initial_rou_asset'] ?? 0)
            );
            $redirectAfterPost = $moduleUrl(['edit' => $leaseId]);
        } elseif ($action === 'upload_ifrs16_payments') {
            if ($slug !== 'ifrs16_leases') {
                throw new RuntimeException('Invalid IFRS16 payment upload action context.');
            }
            app_enforce_current_role_can($slug, 'edit');
            if ($companyId === null || $companyId <= 0) {
                throw new RuntimeException('Company context is required to upload IFRS16 payments.');
            }
            $leaseId = (int) ($_POST['id'] ?? 0);
            if ($leaseId <= 0) {
                throw new RuntimeException('Save the lease before uploading IFRS16 payments.');
            }
            if (!isset($_FILES['ifrs16_payment_file'])) {
                throw new RuntimeException('Please choose the IFRS16 payment file first.');
            }

            $relativeImportPath = app_store_uploaded_file($_FILES['ifrs16_payment_file'], 'imports', ['csv', 'txt', 'xlsx']);
            $absoluteImportPath = app_public_path($relativeImportPath);
            $result = app_import_ifrs16_payment_schedule((int) $companyId, $leaseId, $absoluteImportPath);
            app_flash(
                'success',
                'IFRS16 payment input uploaded. Rows: ' . number_format((int) ($result['rows_count'] ?? 0)) . ', Included in liability: ' . number_format((int) ($result['included_count'] ?? 0)) . ', PV Liability: ' . app_money_format($result['pv_lease_liability'] ?? 0)
            );
            $redirectAfterPost = $moduleUrl(['edit' => $leaseId]);
        } elseif ($action === 'run_prepaid_amortization') {
            if ($slug !== 'prepaid_amortization_runs') {
                throw new RuntimeException('Invalid prepaid amortization action context.');
            }
            app_enforce_current_role_can($slug, 'create');
            if ($companyId === null || $companyId <= 0) {
                throw new RuntimeException('Company context is required to run prepaid amortization.');
            }

            $runResult = app_run_prepaid_amortization_cycle((int) $companyId, $_POST);
            $runsCount = (int) ($runResult['runs_count'] ?? 0);
            $skippedCount = (int) ($runResult['skipped_count'] ?? 0);
            $totalAmount = app_money_format($runResult['total_amount'] ?? 0);
            app_flash(
                'success',
                'Prepaid amortization created as draft. Runs: ' . number_format($runsCount) . ', Skipped: ' . number_format($skippedCount) . ', Amount: ' . $totalAmount
            );
            $redirectAfterPost = $moduleUrl(['edit' => (int) ($runResult['run_id'] ?? 0)]);
        } elseif ($action === 'workflow') {
            $recordId = (int) ($_POST['id'] ?? 0);
            $targetStatus = app_normalize_whitespace((string) ($_POST['target_status'] ?? ''));
            app_enforce_current_role_can($slug, 'post', 'You do not have permission to post or change workflow status in this module.');
            app_module_workflow_transition($slug, $recordId, $companyId, $targetStatus);
            app_flash('success', 'Document status updated successfully.');
            $redirectAfterPost = $slug === 'journal_entries'
                ? $moduleUrl(['view' => $recordId])
                : $moduleUrl(['edit' => $recordId]);
        }
    } catch (Throwable $e) {
        app_flash('error', $e->getMessage());
    }

    app_redirect($redirectAfterPost);
}

$q = app_normalize_whitespace((string) ($_GET['q'] ?? ''));
$dateFilterColumn = app_module_date_filter_column($definition);
$filterFromDate = app_normalize_whitespace((string) ($_GET['from_date'] ?? ''));
$filterToDate = app_normalize_whitespace((string) ($_GET['to_date'] ?? ''));
$invoiceContactFilter = app_normalize_whitespace((string) ($_GET['contact_filter'] ?? ''));
$invoiceContactOptions = [];
$invoiceTaxRateMap = [];
$invoiceWithholdingTaxRateMap = [];
$vendorWithholdingMap = [];
if (in_array($slug, ['sales_invoices', 'student_invoices', 'purchase_bills'], true) && $companyId !== null && $companyId > 0) {
    $invoiceContactOptions = app_module_lookup_options($slug === 'purchase_bills' ? 'vendor_contact_code' : 'customer_contact_code', $companyId);
    $invoiceTaxRateMap = app_tax_rate_map((int) $companyId, $slug === 'purchase_bills' ? 'purchase' : 'sales');
    if ($slug === 'purchase_bills') {
        $invoiceWithholdingTaxRateMap = app_tax_rate_map((int) $companyId, 'withholding');
        $vendorStmt = app_pdo()->prepare(
            "SELECT contact_code, withholding_tax_name, withholding_tax_rate_percent
             FROM contacts
             WHERE company_id = :company_id
               AND contact_type IN ('vendor', 'both')
               AND is_active = 1"
        );
        $vendorStmt->execute(['company_id' => (int) $companyId]);
        foreach (($vendorStmt->fetchAll() ?: []) as $vendorRow) {
            $vendorCode = app_normalize_whitespace((string) ($vendorRow['contact_code'] ?? ''));
            if ($vendorCode === '') {
                continue;
            }
            $vendorWithholdingMap[$vendorCode] = [
                'tax_name' => app_normalize_whitespace((string) ($vendorRow['withholding_tax_name'] ?? '')),
                'rate' => app_number($vendorRow['withholding_tax_rate_percent'] ?? 0),
            ];
        }
    }
} elseif (in_array($slug, ['expenses', 'prepaid_expenses'], true) && $companyId !== null && $companyId > 0) {
    $invoiceTaxRateMap = app_tax_rate_map((int) $companyId, 'purchase');
}
$moduleListFilters = [];
if ($invoiceContactFilter !== '' && in_array($slug, ['sales_invoices', 'student_invoices', 'purchase_bills'], true)) {
    $moduleListFilters['contact_code'] = $invoiceContactFilter;
}
$rows = app_module_list($slug, $companyId, $q, $filterFromDate, $filterToDate, $moduleListFilters);
$bulkStatementGroupOptions = $bulkStatementGroupEnabled ? app_module_lookup_options('financial_statement_group_code', $companyId) : [];
$editId = (int) ($_GET['edit'] ?? 0);
$editing = $editId > 0 ? app_module_find($slug, $editId, $companyId) : null;
$company = $companyId ? app_company_by_id((int) $companyId) : null;
$moduleExportQuery = $_GET;
$moduleExportQuery['module'] = $slug;
if ($companyId !== null && $companyId > 0) {
    $moduleExportQuery['company_id'] = $companyId;
}
$moduleExportQuery['export'] = 'xlsx';
$moduleExportUrl = 'module.php?' . http_build_query($moduleExportQuery);
$moduleSampleQuery = [
    'module' => $slug,
    'sample_import' => 'xlsx',
];
if ($companyId !== null && $companyId > 0) {
    $moduleSampleQuery['company_id'] = $companyId;
}
$moduleSampleUrl = 'module.php?' . http_build_query($moduleSampleQuery);

function module_import_sample_fields(array $definition): array
{
    $autoNumberConfig = app_module_auto_number_config((string) ($definition['slug'] ?? ''));
    $autoNumberField = is_array($autoNumberConfig) ? (string) ($autoNumberConfig['field'] ?? '') : '';
    $fields = [];
    foreach ((array) ($definition['fields'] ?? []) as $field) {
        $fieldName = (string) ($field['name'] ?? '');
        if ($fieldName === '' || $fieldName === 'password_hash') {
            continue;
        }
        if ($fieldName === $autoNumberField) {
            $field['sample_note'] = 'Optional: leave blank to auto-generate.';
        }
        $fields[] = $field;
    }
    return $fields;
}

function module_import_sample_value(array $field, string $slug, ?int $companyId): mixed
{
    $name = (string) ($field['name'] ?? '');
    $type = strtolower((string) ($field['type'] ?? 'text'));
    $lookup = app_normalize_whitespace((string) ($field['lookup'] ?? ''));
    $autoNumberConfig = app_module_auto_number_config($slug);
    $autoNumberField = is_array($autoNumberConfig) ? (string) ($autoNumberConfig['field'] ?? '') : '';
    if ($name === $autoNumberField) {
        return '';
    }

    if ($lookup !== '' && $companyId !== null && $companyId > 0) {
        $options = app_module_lookup_options($lookup, $companyId);
        if ($options) {
            return (string) array_key_first($options);
        }
    }

    $options = app_module_field_options($field);
    if ($options) {
        return (string) array_key_first($options);
    }

    if ($name === 'company_id') {
        return $companyId !== null && $companyId > 0 ? $companyId : '';
    }
    if ($name === 'password_plain') {
        return 'ChangeMe123!';
    }
    if (str_contains($name, 'email')) {
        return 'sample@example.com';
    }
    if (str_contains($name, 'phone')) {
        return '+966500000000';
    }
    if (str_contains($name, 'date')) {
        return date('Y-m-d');
    }
    if (str_contains($name, 'year')) {
        return date('Y');
    }
    if (str_contains($name, 'month')) {
        return (int) date('n');
    }
    if (str_contains($name, 'rate_percent')) {
        return 15;
    }
    if (str_contains($name, 'tax_name')) {
        return (str_contains($slug, 'purchase') || str_contains($slug, 'expense')) ? 'VAT Purchase 15%' : 'VAT Sales 15%';
    }
    if (str_contains($name, 'status')) {
        return in_array($slug, ['sales_invoices', 'student_invoices'], true) ? 'issued' : 'active';
    }
    if ($type === 'checkbox') {
        return 1;
    }
    if ($type === 'number') {
        return 1000;
    }
    return match ($name) {
        'username' => 'sample.user',
        'full_name' => 'Sample User',
        'name' => 'Sample Company',
        'legal_name' => 'Sample Company LLC',
        'currency_code' => 'SAR',
        'contact_name' => 'Sample Contact',
        'contact_type' => 'customer',
        'account_name' => 'Sample Account',
        'account_type' => 'asset',
        'product_name' => 'Sample Product',
        'category' => 'General',
        'unit_name' => 'Unit',
        'bank_name' => 'Sample Bank',
        'center_name' => 'Sample Cost Center',
        'project_name' => 'Sample Project',
        'budget_name' => 'Sample Budget',
        'asset_name' => 'Sample Asset',
        'employee_number' => '',
        'department' => 'Administration',
        'notes' => 'Sample row - replace or delete.',
        default => 'Sample ' . str_replace('_', ' ', $name),
    };
}

function module_import_sample_row(array $fields, string $slug, ?int $companyId, array $overrides = []): array
{
    $row = [];
    foreach ($fields as $field) {
        $name = (string) ($field['name'] ?? '');
        if ($name === '') {
            continue;
        }
        $row[] = array_key_exists($name, $overrides)
            ? $overrides[$name]
            : module_import_sample_value($field, $slug, $companyId);
    }
    return $row;
}

function module_import_sample_sheets(string $slug, array $definition, ?int $companyId): array
{
    $fields = module_import_sample_fields($definition);
    $headers = [];
    $example = [];
    $helpRows = [
        app_xlsx_row(['Import Help - ' . (string) ($definition['title'] ?? $slug)], 'title'),
        ['Use the Template sheet, keep the first row as headers, and add your data below the sample row.'],
        ['Blank auto-number fields will be generated by the system during import.'],
        [],
        app_xlsx_row(['Field Name', 'Label', 'Type', 'Required', 'Options / Lookup', 'Notes'], 'header'),
    ];

    foreach ($fields as $field) {
        $name = (string) ($field['name'] ?? '');
        if ($name === '') {
            continue;
        }
        $headers[] = $name;
        $example[] = module_import_sample_value($field, $slug, $companyId);

        $options = app_module_field_options($field);
        $lookup = app_normalize_whitespace((string) ($field['lookup'] ?? ''));
        $optionText = '';
        if ($options) {
            $optionText = implode(', ', array_map('strval', array_keys($options)));
        } elseif ($lookup !== '' && $companyId !== null && $companyId > 0) {
            $lookupOptions = app_module_lookup_options($lookup, $companyId);
            $optionText = $lookupOptions ? implode(', ', array_slice(array_map('strval', array_keys($lookupOptions)), 0, 8)) : ('Lookup: ' . $lookup);
        }

        $helpRows[] = [
            $name,
            (string) ($field['label'] ?? $name),
            (string) ($field['type'] ?? 'text'),
            !empty($field['required']) ? 'Yes' : 'No',
            $optionText,
            (string) ($field['sample_note'] ?? ''),
        ];
    }

    $templateRows = [
        app_xlsx_row($headers, 'header'),
    ];

    if ($slug === 'chart_of_accounts') {
        $chartSamples = [
            ['account_code' => '1000', 'account_name' => 'Assets', 'account_type' => 'asset', 'parent_code' => '', 'opening_balance' => 0, 'is_active' => 1],
            ['account_code' => '2000', 'account_name' => 'Liabilities', 'account_type' => 'liability', 'parent_code' => '', 'opening_balance' => 0, 'is_active' => 1],
            ['account_code' => '3000', 'account_name' => 'Equity', 'account_type' => 'equity', 'parent_code' => '', 'opening_balance' => 0, 'is_active' => 1],
            ['account_code' => '4000', 'account_name' => 'Revenue', 'account_type' => 'revenue', 'parent_code' => '', 'opening_balance' => 0, 'is_active' => 1],
            ['account_code' => '5000', 'account_name' => 'Expenses', 'account_type' => 'expense', 'parent_code' => '', 'opening_balance' => 0, 'is_active' => 1],
        ];
        foreach ($chartSamples as $sample) {
            $templateRows[] = module_import_sample_row($fields, $slug, $companyId, $sample);
        }
    } else {
        $templateRows[] = $example;
    }

    return [
        [
            'title' => 'Template',
            'rows' => $templateRows,
        ],
        [
            'title' => 'Help',
            'rows' => $helpRows,
        ],
    ];
}

if (app_normalize_whitespace((string) ($_GET['sample_import'] ?? '')) === 'xlsx') {
    if (!$canImport) {
        app_flash('error', 'You do not have permission to import in this module.');
        app_redirect($moduleUrl());
    }
    if (in_array($slug, ['depreciation_runs', 'prepaid_amortization_runs', 'pos_orders', 'pos_sessions', 'pos_cash_movements'], true)) {
        app_flash('error', 'Import sample is not available for automatic entry generation modules.');
        app_redirect($moduleUrl());
    }
    app_export_xlsx(
        'sample-' . preg_replace('/[^a-z0-9\-_]+/i', '-', $slug) . '-import.xlsx',
        module_import_sample_sheets($slug, $definition, $companyId)
    );
}

function module_field_input(array $field, mixed $value, array $definition, array $companies, ?int $companyId, ?array $record = null): string
{
    $name = (string) ($field['name'] ?? '');
    $type = strtolower((string) ($field['type'] ?? 'text'));
    $label = (string) ($field['label'] ?? $name);
    $autoNumberConfig = app_module_auto_number_config((string) ($definition['slug'] ?? ''));
    $autoNumberField = is_array($autoNumberConfig) ? (string) ($autoNumberConfig['field'] ?? '') : '';
    $isAutoNumberField = $autoNumberField !== '' && $name === $autoNumberField;
    $required = !empty($field['required']) && !$isAutoNumberField;
    $requiredAttr = $required ? ' required' : '';
    $escapedValue = app_escape((string) $value);

    if ($name === 'company_id' && (string) ($definition['slug'] ?? '') === 'users') {
        ob_start();
        ?>
        <div class="field">
          <label for="<?= app_escape($name) ?>"><?= app_escape($label) ?></label>
          <select id="<?= app_escape($name) ?>" name="<?= app_escape($name) ?>">
            <option value=""><?= app_escape(app_t('Unassigned')) ?></option>
            <?php foreach ($companies as $company): ?>
              <option value="<?= (int) $company['id'] ?>" <?= (int) $value === (int) $company['id'] ? 'selected' : '' ?>>
                <?= app_escape((string) $company['name']) ?>
              </option>
            <?php endforeach; ?>
          </select>
        </div>
        <?php
        return (string) ob_get_clean();
    }

    $lookupKey = app_normalize_whitespace((string) ($field['lookup'] ?? ''));
    if ($lookupKey !== '') {
        $options = app_module_lookup_options($lookupKey, $companyId);
        $optionMeta = [];
        if ($lookupKey === 'sales_invoice_id' && $companyId !== null && $companyId > 0) {
            foreach (app_sales_invoice_payment_candidates((int) $companyId) as $invoice) {
                $invoiceId = (string) ((int) ($invoice['id'] ?? 0));
                if ($invoiceId === '0') {
                    continue;
                }
                $optionMeta[$invoiceId] = [
                    'invoice_number' => app_normalize_whitespace((string) ($invoice['invoice_number'] ?? '')),
                    'customer_code' => app_normalize_whitespace((string) ($invoice['customer_code'] ?? '')),
                    'remaining' => app_sales_invoice_remaining_amount($invoice),
                ];
            }
        }
        if ($lookupKey === 'purchase_bill_id' && $companyId !== null && $companyId > 0) {
            foreach (app_purchase_bill_payment_candidates((int) $companyId) as $bill) {
                $billId = (string) ((int) ($bill['id'] ?? 0));
                if ($billId === '0') {
                    continue;
                }
                $optionMeta[$billId] = [
                    'bill_number' => app_normalize_whitespace((string) ($bill['bill_number'] ?? '')),
                    'vendor_code' => app_normalize_whitespace((string) ($bill['vendor_code'] ?? '')),
                    'remaining' => app_purchase_bill_remaining_amount($bill),
                ];
            }
        }
        if ($lookupKey === 'expense_id' && $companyId !== null && $companyId > 0) {
            foreach (app_expense_payment_candidates((int) $companyId) as $expense) {
                $expenseId = (string) ((int) ($expense['id'] ?? 0));
                if ($expenseId === '0') {
                    continue;
                }
                $optionMeta[$expenseId] = [
                    'expense_number' => app_normalize_whitespace((string) ($expense['expense_number'] ?? '')),
                    'payee_code' => app_normalize_whitespace((string) ($expense['payee_code'] ?? '')),
                    'remaining' => app_expense_remaining_amount($expense),
                ];
            }
        }
        if ($lookupKey === 'ifrs16_schedule_id' && $companyId !== null && $companyId > 0) {
            $currentPaymentId = (string) ($definition['slug'] ?? '') === 'payments' ? (int) ($record['id'] ?? 0) : 0;
            $stmt = app_pdo()->prepare(
                "SELECT s.id, s.lease_id, s.lease_number, s.sequence_no, s.schedule_date, s.payment_amount, s.status,
                        l.lessor_code
                 FROM ifrs16_lease_schedules s
                 INNER JOIN ifrs16_leases l
                         ON l.company_id = s.company_id
                        AND l.id = s.lease_id
                 WHERE s.company_id = :company_id
                   AND l.status NOT IN ('draft', 'cancelled')"
            );
            $stmt->execute(['company_id' => (int) $companyId]);
            foreach (($stmt->fetchAll() ?: []) as $schedule) {
                $scheduleId = (string) ((int) ($schedule['id'] ?? 0));
                if ($scheduleId === '0') {
                    continue;
                }
                $optionMeta[$scheduleId] = [
                    'lease_id' => (int) ($schedule['lease_id'] ?? 0),
                    'lease_number' => app_normalize_whitespace((string) ($schedule['lease_number'] ?? '')),
                    'sequence_no' => app_integer($schedule['sequence_no'] ?? 0),
                    'schedule_date' => app_normalize_whitespace((string) ($schedule['schedule_date'] ?? '')),
                    'lessor_code' => app_normalize_whitespace((string) ($schedule['lessor_code'] ?? '')),
                    'payment_amount' => round(max(0, app_number($schedule['payment_amount'] ?? 0)), 2),
                    'remaining' => app_ifrs16_schedule_remaining_amount((int) $companyId, $schedule, $currentPaymentId > 0 ? $currentPaymentId : null),
                    'status' => app_normalize_whitespace((string) ($schedule['status'] ?? '')),
                ];
            }
        }
        if ($lookupKey === 'student_grade_code' && $companyId !== null && $companyId > 0) {
            $stmt = app_pdo()->prepare(
                'SELECT grade_code, stage_code
                 FROM student_grades
                 WHERE company_id = :company_id AND is_active = 1'
            );
            $stmt->execute(['company_id' => (int) $companyId]);
            foreach (($stmt->fetchAll() ?: []) as $grade) {
                $gradeCode = app_normalize_whitespace((string) ($grade['grade_code'] ?? ''));
                if ($gradeCode !== '') {
                    $optionMeta[$gradeCode] = ['stage_code' => app_normalize_whitespace((string) ($grade['stage_code'] ?? ''))];
                }
            }
        }
        if ($lookupKey === 'student_class_code' && $companyId !== null && $companyId > 0) {
            $stmt = app_pdo()->prepare(
                'SELECT class_code, stage_code, grade_code
                 FROM student_classes
                 WHERE company_id = :company_id AND is_active = 1'
            );
            $stmt->execute(['company_id' => (int) $companyId]);
            foreach (($stmt->fetchAll() ?: []) as $class) {
                $classCode = app_normalize_whitespace((string) ($class['class_code'] ?? ''));
                if ($classCode !== '') {
                    $optionMeta[$classCode] = [
                        'stage_code' => app_normalize_whitespace((string) ($class['stage_code'] ?? '')),
                        'grade_code' => app_normalize_whitespace((string) ($class['grade_code'] ?? '')),
                    ];
                }
            }
        }
        if ($lookupKey === 'student_number' && $companyId !== null && $companyId > 0) {
            $stmt = app_pdo()->prepare(
                'SELECT student_number, guardian_contact_code, stage_code, grade_code, class_code
                 FROM students
                 WHERE company_id = :company_id AND status = :status'
            );
            $stmt->execute(['company_id' => (int) $companyId, 'status' => 'active']);
            foreach (($stmt->fetchAll() ?: []) as $student) {
                $studentNumber = app_normalize_whitespace((string) ($student['student_number'] ?? ''));
                if ($studentNumber !== '') {
                    $optionMeta[$studentNumber] = [
                        'guardian_contact_code' => app_normalize_whitespace((string) ($student['guardian_contact_code'] ?? '')),
                        'stage_code' => app_normalize_whitespace((string) ($student['stage_code'] ?? '')),
                        'grade_code' => app_normalize_whitespace((string) ($student['grade_code'] ?? '')),
                        'class_code' => app_normalize_whitespace((string) ($student['class_code'] ?? '')),
                    ];
                }
            }
        }
        if ($lookupKey === 'student_revenue_type_code' && $companyId !== null && $companyId > 0) {
            $stmt = app_pdo()->prepare(
                'SELECT revenue_type_code, default_amount, is_taxable, tax_name, revenue_account_code, cost_center_code
                 FROM student_revenue_types
                 WHERE company_id = :company_id AND is_active = 1'
            );
            $stmt->execute(['company_id' => (int) $companyId]);
            $taxRates = app_tax_rate_map((int) $companyId, 'sales');
            foreach (($stmt->fetchAll() ?: []) as $typeRow) {
                $typeCode = app_normalize_whitespace((string) ($typeRow['revenue_type_code'] ?? ''));
                if ($typeCode !== '') {
                    $taxName = app_normalize_whitespace((string) ($typeRow['tax_name'] ?? ''));
                    $optionMeta[$typeCode] = [
                        'default_amount' => app_number($typeRow['default_amount'] ?? 0),
                        'is_taxable' => (int) ($typeRow['is_taxable'] ?? 0),
                        'tax_name' => $taxName,
                        'tax_rate' => $taxName !== '' ? app_number($taxRates[$taxName] ?? 0) : 0,
                        'revenue_account_code' => app_normalize_whitespace((string) ($typeRow['revenue_account_code'] ?? '')),
                        'cost_center_code' => app_normalize_whitespace((string) ($typeRow['cost_center_code'] ?? '')),
                    ];
                }
            }
        }
        if ($lookupKey === 'student_invoice_id' && $companyId !== null && $companyId > 0) {
            foreach (app_student_invoice_payment_candidates((int) $companyId) as $invoice) {
                $invoiceId = (string) ((int) ($invoice['id'] ?? 0));
                if ($invoiceId === '0') {
                    continue;
                }
                $optionMeta[$invoiceId] = [
                    'student_invoice_number' => app_normalize_whitespace((string) ($invoice['student_invoice_number'] ?? '')),
                    'student_number' => app_normalize_whitespace((string) ($invoice['student_number'] ?? '')),
                    'guardian_contact_code' => app_normalize_whitespace((string) ($invoice['guardian_contact_code'] ?? '')),
                    'remaining' => app_student_invoice_remaining_amount($invoice),
                ];
            }
        }
        if ($lookupKey === 'payment_account_name' && $companyId !== null && $companyId > 0) {
            foreach (app_payment_account_category_map((int) $companyId) as $accountName => $category) {
                $optionMeta[(string) $accountName] = ['payment_category' => $category];
            }
        }
        if (in_array($lookupKey, ['sales_tax_name', 'purchase_tax_name', 'all_tax_name'], true) && $companyId !== null && $companyId > 0) {
            $scope = match ($lookupKey) {
                'sales_tax_name' => 'sales',
                'purchase_tax_name' => 'purchase',
                default => 'both',
            };
            foreach (app_tax_rate_map((int) $companyId, $scope) as $taxName => $ratePercent) {
                $optionMeta[(string) $taxName] = ['tax_rate' => $ratePercent];
            }
        }
        ob_start();
        $selectAttrs = $requiredAttr;
        if ($lookupKey === 'payment_account_name') {
            $selectAttrs .= ' data-payment-account-select';
        }
        ?>
        <div class="field">
          <label for="<?= app_escape($name) ?>"><?= app_escape($label) ?></label>
          <select id="<?= app_escape($name) ?>" name="<?= app_escape($name) ?>"<?= $selectAttrs ?>>
            <option value="">Choose</option>
            <?php foreach ($options as $optionValue => $optionLabel): ?>
              <?php
                $meta = $optionMeta[(string) $optionValue] ?? null;
                $displayOptionLabel = (string) $optionLabel;
                $dataAttrs = '';
                if ($meta !== null) {
                    if (array_key_exists('invoice_number', $meta)) {
                        $dataAttrs .= ' data-invoice-number="' . app_escape((string) ($meta['invoice_number'] ?? '')) . '"';
                        $dataAttrs .= ' data-customer-code="' . app_escape((string) ($meta['customer_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('bill_number', $meta)) {
                        $dataAttrs .= ' data-bill-number="' . app_escape((string) ($meta['bill_number'] ?? '')) . '"';
                        $dataAttrs .= ' data-vendor-code="' . app_escape((string) ($meta['vendor_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('expense_number', $meta)) {
                        $dataAttrs .= ' data-expense-number="' . app_escape((string) ($meta['expense_number'] ?? '')) . '"';
                        $dataAttrs .= ' data-payee-code="' . app_escape((string) ($meta['payee_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('lease_id', $meta)) {
                        $dataAttrs .= ' data-lease-id="' . app_escape((string) ($meta['lease_id'] ?? '')) . '"';
                    }
                    if (array_key_exists('lease_number', $meta)) {
                        $dataAttrs .= ' data-lease-number="' . app_escape((string) ($meta['lease_number'] ?? '')) . '"';
                    }
                    if (array_key_exists('sequence_no', $meta)) {
                        $dataAttrs .= ' data-sequence-no="' . app_escape((string) ($meta['sequence_no'] ?? 0)) . '"';
                    }
                    if (array_key_exists('schedule_date', $meta)) {
                        $dataAttrs .= ' data-schedule-date="' . app_escape((string) ($meta['schedule_date'] ?? '')) . '"';
                    }
                    if (array_key_exists('lessor_code', $meta)) {
                        $dataAttrs .= ' data-lessor-code="' . app_escape((string) ($meta['lessor_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('payment_amount', $meta)) {
                        $dataAttrs .= ' data-payment-amount="' . app_escape((string) ($meta['payment_amount'] ?? 0)) . '"';
                    }
                    if (array_key_exists('remaining', $meta)) {
                        $dataAttrs .= ' data-remaining="' . app_escape((string) ($meta['remaining'] ?? 0)) . '"';
                    }
                    if (array_key_exists('student_invoice_number', $meta)) {
                        $dataAttrs .= ' data-student-invoice-number="' . app_escape((string) ($meta['student_invoice_number'] ?? '')) . '"';
                    }
                    if (array_key_exists('student_number', $meta)) {
                        $dataAttrs .= ' data-student-number="' . app_escape((string) ($meta['student_number'] ?? '')) . '"';
                    }
                    if (array_key_exists('guardian_contact_code', $meta)) {
                        $dataAttrs .= ' data-guardian-contact-code="' . app_escape((string) ($meta['guardian_contact_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('stage_code', $meta)) {
                        $dataAttrs .= ' data-stage-code="' . app_escape((string) ($meta['stage_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('grade_code', $meta)) {
                        $dataAttrs .= ' data-grade-code="' . app_escape((string) ($meta['grade_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('class_code', $meta)) {
                        $dataAttrs .= ' data-class-code="' . app_escape((string) ($meta['class_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('default_amount', $meta)) {
                        $dataAttrs .= ' data-default-amount="' . app_escape((string) ($meta['default_amount'] ?? 0)) . '"';
                    }
                    if (array_key_exists('is_taxable', $meta)) {
                        $dataAttrs .= ' data-is-taxable="' . app_escape((string) ((int) ($meta['is_taxable'] ?? 0))) . '"';
                    }
                    if (array_key_exists('tax_name', $meta)) {
                        $dataAttrs .= ' data-tax-name="' . app_escape((string) ($meta['tax_name'] ?? '')) . '"';
                    }
                    if (array_key_exists('revenue_account_code', $meta)) {
                        $dataAttrs .= ' data-revenue-account-code="' . app_escape((string) ($meta['revenue_account_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('cost_center_code', $meta)) {
                        $dataAttrs .= ' data-cost-center-code="' . app_escape((string) ($meta['cost_center_code'] ?? '')) . '"';
                    }
                    if (array_key_exists('tax_rate', $meta)) {
                        $dataAttrs .= ' data-tax-rate="' . app_escape((string) ($meta['tax_rate'] ?? 0)) . '"';
                    }
                    if (array_key_exists('payment_category', $meta)) {
                        $dataAttrs .= ' data-payment-category="' . app_escape((string) ($meta['payment_category'] ?? '')) . '"';
                    }
                    if ($lookupKey === 'ifrs16_schedule_id') {
                        $parts = [];
                        $leaseNumber = app_normalize_whitespace((string) ($meta['lease_number'] ?? ''));
                        if ($leaseNumber !== '') {
                            $parts[] = $leaseNumber;
                        }
                        $sequence = app_integer($meta['sequence_no'] ?? 0);
                        if ($sequence > 0) {
                            $parts[] = '#' . str_pad((string) $sequence, 3, '0', STR_PAD_LEFT);
                        }
                        $scheduleDate = app_normalize_whitespace((string) ($meta['schedule_date'] ?? ''));
                        if ($scheduleDate !== '') {
                            $parts[] = $scheduleDate;
                        }
                        $parts[] = 'Payment ' . app_money_format($meta['payment_amount'] ?? 0);
                        $parts[] = 'Remaining ' . app_money_format($meta['remaining'] ?? 0);
                        $status = app_normalize_whitespace((string) ($meta['status'] ?? ''));
                        if ($status !== '') {
                            $parts[] = ucfirst(str_replace('_', ' ', $status));
                        }
                        if ($parts) {
                            $displayOptionLabel = implode(' - ', $parts);
                        }
                    }
                }
              ?>
              <option value="<?= app_escape((string) $optionValue) ?>"<?= $dataAttrs ?> <?= (string) $value === (string) $optionValue ? 'selected' : '' ?>>
                <?= app_escape($displayOptionLabel) ?>
              </option>
            <?php endforeach; ?>
          </select>
        </div>
        <?php
        return (string) ob_get_clean();
    }

    if ($type === 'textarea') {
        return '<div class="field"><label for="' . app_escape($name) . '">' . app_escape($label) . '</label><textarea id="' . app_escape($name) . '" name="' . app_escape($name) . '"' . $requiredAttr . '>' . $escapedValue . '</textarea></div>';
    }

    if ($type === 'select') {
        $options = app_module_field_options($field);
        $selectAttrs = $requiredAttr;
        if ($name === 'payment_method') {
            $selectAttrs .= ' data-payment-method-select';
        }
        ob_start();
        ?>
        <div class="field">
          <label for="<?= app_escape($name) ?>"><?= app_escape($label) ?></label>
          <select id="<?= app_escape($name) ?>" name="<?= app_escape($name) ?>"<?= $selectAttrs ?>>
            <option value="">Choose</option>
            <?php foreach ($options as $optionValue => $optionLabel): ?>
              <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) $value === (string) $optionValue ? 'selected' : '' ?>>
                <?= app_escape((string) $optionLabel) ?>
              </option>
            <?php endforeach; ?>
          </select>
        </div>
        <?php
        return (string) ob_get_clean();
    }

    if ($type === 'checkbox') {
        $checked = (int) $value === 1 ? ' checked' : '';
        return '<div class="field checkbox-field"><label for="' . app_escape($name) . '" style="display:flex;align-items:center;gap:8px;"><input id="' . app_escape($name) . '" type="checkbox" name="' . app_escape($name) . '" value="1"' . $checked . '> ' . app_escape($label) . '</label></div>';
    }

    $htmlType = match ($type) {
        'number' => 'number',
        'date' => 'date',
        'password' => 'password',
        default => 'text',
    };
    $stepAttr = $htmlType === 'number' ? ' step="0.01"' : '';
    $valueAttr = $htmlType === 'password' ? '' : ' value="' . $escapedValue . '"';

    $html = '<div class="field"><label for="' . app_escape($name) . '">' . app_escape($label) . '</label><input id="' . app_escape($name) . '" name="' . app_escape($name) . '" type="' . $htmlType . '"' . $stepAttr . $valueAttr . $requiredAttr . '>';
    if ($isAutoNumberField) {
        $html .= '<div class="field-help">Leave blank to generate sequential number automatically.</div>';
    }
    $html .= '</div>';
    return $html;
}

function module_chart_account_tree_data(array $rows): array
{
    $rowsByCode = [];
    foreach ($rows as $row) {
        $code = app_normalize_whitespace((string) ($row['account_code'] ?? ''));
        if ($code === '') {
            continue;
        }
        $row['account_code'] = $code;
        $row['parent_code'] = app_normalize_whitespace((string) ($row['parent_code'] ?? ''));
        $rowsByCode[$code] = $row;
    }

    $childrenByParent = ['' => []];
    foreach ($rowsByCode as $code => $row) {
        $parentCode = app_normalize_whitespace((string) ($row['parent_code'] ?? ''));
        if ($parentCode !== '' && !isset($rowsByCode[$parentCode])) {
            $parentCode = '';
        }
        if (!isset($childrenByParent[$parentCode])) {
            $childrenByParent[$parentCode] = [];
        }
        $childrenByParent[$parentCode][] = $row;
    }

    foreach ($childrenByParent as &$children) {
        usort($children, static function (array $a, array $b): int {
            return strnatcasecmp((string) ($a['account_code'] ?? ''), (string) ($b['account_code'] ?? ''));
        });
    }
    unset($children);

    return [
        'rows_by_code' => $rowsByCode,
        'children_by_parent' => $childrenByParent,
        'roots' => $childrenByParent[''] ?? [],
    ];
}

function module_chart_numeric_step(string $code): int
{
    if ($code === '' || !ctype_digit($code)) {
        return 1;
    }
    $zeroCount = strlen($code) - strlen(rtrim($code, '0'));
    if ($zeroCount <= 0) {
        return 1;
    }
    return (int) (10 ** max(0, $zeroCount - 1));
}

function module_chart_first_child_code(string $parentCode): string
{
    $parentCode = app_normalize_whitespace($parentCode);
    if ($parentCode === '') {
        return '';
    }
    if (!ctype_digit($parentCode)) {
        return $parentCode . '-001';
    }

    $step = module_chart_numeric_step($parentCode);
    if ($step > 1 || str_ends_with($parentCode, '0')) {
        return (string) ((int) $parentCode + $step);
    }
    return $parentCode . '1';
}

function module_chart_next_child_code(string $parentCode, array $childrenByParent, array $rowsByCode): string
{
    $parentCode = app_normalize_whitespace($parentCode);
    if ($parentCode === '') {
        return '';
    }

    $children = (array) ($childrenByParent[$parentCode] ?? []);
    if (!$children) {
        $candidate = module_chart_first_child_code($parentCode);
        while ($candidate !== '' && isset($rowsByCode[$candidate]) && ctype_digit($candidate)) {
            $candidate = (string) ((int) $candidate + module_chart_numeric_step($candidate));
        }
        return $candidate;
    }

    $maxNumericCode = null;
    $maxPrefixSuffix = null;
    $prefixSuffixWidth = 0;
    foreach ($children as $child) {
        $childCode = app_normalize_whitespace((string) ($child['account_code'] ?? ''));
        if ($childCode === '') {
            continue;
        }
        if (ctype_digit($childCode)) {
            $childNumber = (int) $childCode;
            $maxNumericCode = $maxNumericCode === null ? $childNumber : max($maxNumericCode, $childNumber);
        }
        if (str_starts_with($childCode, $parentCode)) {
            $suffix = substr($childCode, strlen($parentCode));
            if ($suffix !== '' && ctype_digit($suffix)) {
                $suffixNumber = (int) $suffix;
                $maxPrefixSuffix = $maxPrefixSuffix === null ? $suffixNumber : max($maxPrefixSuffix, $suffixNumber);
                $prefixSuffixWidth = max($prefixSuffixWidth, strlen($suffix));
            }
        }
    }

    if (ctype_digit($parentCode) && $maxPrefixSuffix !== null) {
        $candidate = $parentCode . str_pad((string) ($maxPrefixSuffix + 1), max(1, $prefixSuffixWidth), '0', STR_PAD_LEFT);
    } elseif ($maxNumericCode !== null) {
        $candidate = (string) ($maxNumericCode + module_chart_numeric_step((string) $maxNumericCode));
    } else {
        $candidate = module_chart_first_child_code($parentCode);
    }

    while ($candidate !== '' && isset($rowsByCode[$candidate]) && ctype_digit($candidate)) {
        $candidate = (string) ((int) $candidate + module_chart_numeric_step($candidate));
    }

    return $candidate;
}

function module_chart_account_node_html(
    array $row,
    int $depth,
    array $childrenByParent,
    array $rowsByCode,
    array $codeChildrenByParent,
    array $codeRowsByCode,
    callable $moduleUrl,
    bool $canCreate,
    bool $canEdit,
    bool $canDelete,
    bool $bulkSelectionEnabled,
    ?int $companyId,
    array $definition,
    array $companies
): string {
    $rowId = (int) ($row['id'] ?? 0);
    $code = app_normalize_whitespace((string) ($row['account_code'] ?? ''));
    $name = app_normalize_whitespace((string) ($row['account_name'] ?? ''));
    $type = strtolower(app_normalize_whitespace((string) ($row['account_type'] ?? '')));
    $groupCode = app_normalize_whitespace((string) ($row['financial_statement_group_code'] ?? ''));
    $children = (array) ($childrenByParent[$code] ?? []);
    $hasChildren = count($children) > 0;
    $initialCollapsed = $hasChildren && $depth >= 1;
    $nextChildCode = module_chart_next_child_code($code, $codeChildrenByParent, $codeRowsByCode);
    $typeOptions = [];
    foreach ((array) ($definition['fields'] ?? []) as $field) {
        if ((string) ($field['name'] ?? '') === 'account_type') {
            $typeOptions = app_module_field_options($field);
            break;
        }
    }
    $typeLabel = (string) ($typeOptions[$type] ?? ucfirst($type));

    ob_start();
    ?>
    <div class="coa-tree-node<?= $initialCollapsed ? ' is-collapsed' : '' ?><?= $depth === 0 ? ' is-root' : '' ?>" data-coa-node data-depth="<?= (int) $depth ?>">
      <div class="coa-tree-row<?= $hasChildren ? ' has-children' : ' is-leaf' ?>" style="--coa-depth: <?= (int) $depth ?>">
        <?php if ($bulkSelectionEnabled): ?>
          <label class="coa-tree-select print-hide">
            <input
              type="checkbox"
              name="record_ids[]"
              value="<?= $rowId ?>"
              form="moduleBulkDeleteForm"
              data-bulk-checkbox
            >
          </label>
        <?php endif; ?>
        <?php if ($hasChildren): ?>
          <button class="coa-tree-toggle" type="button" data-coa-toggle aria-expanded="<?= $initialCollapsed ? 'false' : 'true' ?>" title="<?= app_escape(app_t('Open / close')) ?>">
            <span data-coa-toggle-symbol><?= $initialCollapsed ? '+' : '-' ?></span>
          </button>
        <?php else: ?>
          <span class="coa-tree-toggle-spacer" aria-hidden="true"></span>
        <?php endif; ?>
        <div class="coa-tree-main">
          <span class="coa-tree-code"><?= app_escape($code) ?></span>
          <span class="coa-tree-name"><?= app_escape($name) ?></span>
        </div>
        <div class="coa-tree-meta">
          <span class="coa-tree-pill"><?= app_escape($typeLabel) ?></span>
          <span><?= module_cell_value('chart_of_accounts', 'financial_statement_group_code', $groupCode, $definition, $companies, $companyId) ?></span>
          <span class="coa-tree-amount"><?= module_cell_value('chart_of_accounts', 'opening_balance', $row['opening_balance'] ?? 0, $definition, $companies, $companyId) ?></span>
          <span><?= module_cell_value('chart_of_accounts', 'is_active', $row['is_active'] ?? 0, $definition, $companies, $companyId) ?></span>
        </div>
        <div class="coa-tree-actions print-hide">
          <?php if ($canCreate): ?>
            <button
              class="icon-btn coa-tree-add"
              type="button"
              data-coa-add-child
              data-parent-code="<?= app_escape($code) ?>"
              data-parent-name="<?= app_escape($name) ?>"
              data-account-type="<?= app_escape($type) ?>"
              data-account-type-label="<?= app_escape($typeLabel) ?>"
              data-next-code="<?= app_escape($nextChildCode) ?>"
              data-statement-group="<?= app_escape($groupCode) ?>"
              title="<?= app_escape(app_t('Add sub account')) ?>"
            >+</button>
          <?php endif; ?>
          <?php if ($canEdit): ?>
            <a class="icon-btn" href="<?= app_escape($moduleUrl(['edit' => $rowId])) ?>"><?= app_escape(app_t('Edit')) ?></a>
          <?php endif; ?>
          <?php if ($canDelete): ?>
            <form method="post" onsubmit="return confirm('Delete this record?');">
              <?= app_csrf_field() ?>
              <input type="hidden" name="action" value="delete">
              <input type="hidden" name="id" value="<?= $rowId ?>">
              <?php if ($companyId !== null): ?>
                <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
              <?php endif; ?>
              <button class="icon-btn danger" type="submit"><?= app_escape(app_t('Delete')) ?></button>
            </form>
          <?php endif; ?>
        </div>
      </div>
      <?php if ($hasChildren): ?>
        <div class="coa-tree-children">
          <?php foreach ($children as $child): ?>
            <?= module_chart_account_node_html($child, $depth + 1, $childrenByParent, $rowsByCode, $codeChildrenByParent, $codeRowsByCode, $moduleUrl, $canCreate, $canEdit, $canDelete, $bulkSelectionEnabled, $companyId, $definition, $companies) ?>
          <?php endforeach; ?>
        </div>
      <?php endif; ?>
    </div>
    <?php
    return (string) ob_get_clean();
}

function module_user_company_access_input(array $companies, array $selectedIds): string
{
    $selectedMap = [];
    foreach ($selectedIds as $selectedId) {
        $selectedId = (int) $selectedId;
        if ($selectedId > 0) {
            $selectedMap[$selectedId] = true;
        }
    }

    ob_start();
    ?>
    <div class="field field-wide">
      <label><?= app_escape(app_t('Allowed Companies')) ?></label>
      <div class="field-help"><?= app_escape(app_t('Choose one or more companies this user can access.')) ?></div>
      <?php if (app_is_admin()): ?>
        <div class="field-help"><?= app_escape(app_t('Admins can access all active companies.')) ?></div>
      <?php endif; ?>
      <?php if (!$companies): ?>
        <div class="empty-state"><?= app_escape(app_t('No companies available for this user.')) ?></div>
      <?php else: ?>
        <div class="company-access-grid">
          <?php foreach ($companies as $company): ?>
            <?php $companyId = (int) ($company['id'] ?? 0); ?>
            <label class="company-access-option">
              <input type="checkbox" name="company_access_ids[]" value="<?= $companyId ?>" <?= isset($selectedMap[$companyId]) ? 'checked' : '' ?>>
              <span>
                <strong><?= app_escape((string) ($company['name'] ?? '')) ?></strong>
                <small><?= app_escape((string) ($company['currency_code'] ?? '')) ?></small>
              </span>
            </label>
          <?php endforeach; ?>
        </div>
      <?php endif; ?>
    </div>
    <?php
    return (string) ob_get_clean();
}

function module_cell_value(string $slug, string $column, mixed $value, array $definition, array $companies, ?int $companyId): string
{
    if ($column === 'company_id') {
        $companyId = (int) $value;
        foreach ($companies as $company) {
            if ((int) $company['id'] === $companyId) {
                return app_escape((string) $company['name']);
            }
        }
        return 'N/A';
    }

    if ($column === 'role') {
        return app_escape(app_role_label((string) $value));
    }

    if ($slug === 'journal_entries' && $column === 'source_type') {
        $sourceType = app_normalize_whitespace((string) $value);
        if ($sourceType === '') {
            return 'Manual';
        }
        return app_escape(app_source_module_label($sourceType));
    }

    if ($slug === 'journal_entries' && $column === 'line_items_count') {
        return app_escape(number_format((int) $value));
    }

    if ($slug === 'journal_entries' && in_array($column, ['line_items_total_debit', 'line_items_total_credit'], true)) {
        return app_escape(app_money_format(app_number($value)));
    }

    if (in_array($slug, ['purchase_bills', 'expenses'], true) && in_array($column, ['budget_remaining', 'budget_amount', 'budget_spent'], true)) {
        $amount = app_number($value);
        $class = $column === 'budget_remaining' && $amount < 0 ? 'badge badge-red' : 'badge badge-green';
        return '<span class="' . app_escape($class) . '">' . app_escape(app_money_format($amount)) . '</span>';
    }

    if ($column === 'logo_path') {
        $path = app_normalize_whitespace((string) $value);
        if ($path === '') {
            return 'N/A';
        }
        return '<img src="' . app_escape($path) . '" alt="Logo" style="width:56px;height:56px;border-radius:10px;background:#fff;padding:4px;border:1px solid rgba(45,10,107,.12);object-fit:contain;">';
    }

    $field = app_module_field_by_name($definition, $column);
    if ($field) {
        $lookupKey = app_normalize_whitespace((string) ($field['lookup'] ?? ''));
        if ($lookupKey !== '') {
            $options = app_module_lookup_options($lookupKey, $companyId);
            $raw = app_normalize_whitespace((string) $value);
            if ($raw === '') {
                return 'N/A';
            }
            return app_escape($options[$raw] ?? $raw);
        }
        return app_escape(app_module_value_label($slug, $field, $value));
    }

    return app_escape((string) $value);
}

function module_record_display_name(string $slug, array $record, string $fallback): string
{
    $candidates = [
        'sales_invoices' => 'invoice_number',
        'purchase_bills' => 'bill_number',
        'expenses' => 'expense_number',
        'payments' => 'payment_number',
        'journal_entries' => 'entry_number',
        'depreciation_runs' => 'run_number',
        'ifrs16_leases' => 'lease_number',
        'ifrs16_lease_schedules' => 'lease_number',
        'assets' => 'asset_code',
        'cash_accounts' => 'cash_name',
        'contacts' => 'contact_name',
        'products' => 'product_name',
        'companies' => 'name',
        'excellence_hospitals' => 'hospital_name',
        'excellence_devices' => 'device_name',
        'excellence_operations' => 'operation_number',
        'excellence_hospital_expenses' => 'expense_number',
        'excellence_admin_expenses' => 'expense_number',
        'excellence_salaries' => 'salary_number',
    ];
    $field = $candidates[$slug] ?? '';
    $value = $field !== '' ? app_normalize_whitespace((string) ($record[$field] ?? '')) : '';
    if ($value !== '') {
        return $value;
    }
    $id = (int) ($record['id'] ?? 0);
    return $id > 0 ? ($fallback . ' #' . $id) : 'New ' . $fallback;
}

function module_status_label_class(string $status): string
{
    return match ($status) {
        'posted', 'issued', 'approved', 'paid' => 'is-done',
        'partially_paid' => 'is-progress',
        'cancelled', 'reversed' => 'is-cancelled',
        default => 'is-draft',
    };
}

function module_statusbar_html(string $slug, array $record): string
{
    $workflow = app_module_workflow_definition($slug);
    if (!$workflow || !$record) {
        return '';
    }

    $field = (string) ($workflow['field'] ?? 'status');
    $current = app_normalize_whitespace((string) ($record[$field] ?? ($workflow['default'] ?? 'draft')));
    $states = (array) ($workflow['states'] ?? []);
    if (!$states) {
        return '';
    }

    $html = '<div class="erp-statusbar" aria-label="Document status">';
    foreach ($states as $state => $label) {
        $class = 'erp-status-step ' . module_status_label_class((string) $state);
        if ((string) $state === $current) {
            $class .= ' is-current';
        }
        $html .= '<span class="' . app_escape($class) . '">' . app_escape((string) $label) . '</span>';
    }
    $html .= '</div>';
    return $html;
}

function module_workflow_actions_html(string $slug, array $record, ?int $companyId): string
{
    if (!app_current_role_can($slug, 'post')) {
        return '';
    }

    $workflow = app_module_workflow_definition($slug);
    if (!$workflow || !$record) {
        return '';
    }

    $field = (string) ($workflow['field'] ?? 'status');
    $current = app_normalize_whitespace((string) ($record[$field] ?? ($workflow['default'] ?? 'draft')));
    $actions = (array) ($workflow['actions'] ?? []);
    if (!$actions) {
        return '';
    }

    $html = '';
    foreach ($actions as $targetStatus => $action) {
        $allowedFrom = (array) ($action['from'] ?? []);
        if ($allowedFrom && !in_array($current, $allowedFrom, true)) {
            continue;
        }
        $classes = 'btn btn-secondary';
        if (!empty($action['primary'])) {
            $classes = 'btn btn-primary';
        } elseif (!empty($action['danger'])) {
            $classes = 'btn btn-danger';
        }

        $html .= '<form method="post" class="inline-workflow-form">';
        $html .= app_csrf_field();
        $html .= '<input type="hidden" name="action" value="workflow">';
        $html .= '<input type="hidden" name="id" value="' . (int) ($record['id'] ?? 0) . '">';
        $html .= '<input type="hidden" name="target_status" value="' . app_escape((string) $targetStatus) . '">';
        if ($companyId !== null) {
            $html .= '<input type="hidden" name="company_id" value="' . (int) $companyId . '">';
        }
        $html .= '<button class="' . app_escape($classes) . '" type="submit">' . app_escape((string) ($action['label'] ?? $targetStatus)) . '</button>';
        $html .= '</form>';
    }
    return $html;
}

function module_smart_buttons_html(string $slug, array $record, ?int $companyId): string
{
    if (!$record || $companyId === null || $companyId <= 0) {
        return '';
    }

    $buttons = [];
    if (app_module_requires_auto_posting($slug)) {
        $journal = app_journal_entry_for_source((int) $companyId, $slug, (int) ($record['id'] ?? 0));
        if ($journal) {
            $buttons[] = [
                'label' => 'Journal Entry',
                'value' => (string) ($journal['entry_number'] ?? 'Open'),
                'href' => 'module.php?module=journal_entries&view=' . (int) ($journal['id'] ?? 0) . '&company_id=' . (int) $companyId,
            ];
        }
    }

    if ($slug === 'ifrs16_leases') {
        $leaseId = (int) ($record['id'] ?? 0);
        $journal = app_journal_entry_for_source((int) $companyId, 'ifrs16_leases', $leaseId);
        if ($journal) {
            $buttons[] = [
                'label' => 'Initial Journal',
                'value' => (string) ($journal['entry_number'] ?? 'Open'),
                'href' => 'module.php?module=journal_entries&view=' . (int) ($journal['id'] ?? 0) . '&company_id=' . (int) $companyId,
            ];
        }
        $scheduleCount = count(app_ifrs16_lease_schedule_lines((int) $companyId, $leaseId));
        if ($scheduleCount > 0) {
            $buttons[] = [
                'label' => 'Schedule Lines',
                'value' => number_format($scheduleCount),
                'href' => 'module.php?module=ifrs16_lease_schedules&q=' . urlencode((string) ($record['lease_number'] ?? '')) . '&company_id=' . (int) $companyId,
            ];
        }
    }

    if ($slug === 'sales_invoices') {
        $status = strtolower(app_normalize_whitespace((string) ($record['status'] ?? '')));
        $remaining = app_sales_invoice_remaining_amount($record);
        if ($remaining > 0 && !in_array($status, ['draft', 'cancelled'], true)) {
            $buttons[] = [
                'label' => 'Register Payment',
                'value' => app_money_format($remaining),
                'href' => 'module.php?module=payments&sales_invoice_id=' . (int) ($record['id'] ?? 0) . '&company_id=' . (int) $companyId,
            ];
        }
    }

    if ($slug === 'purchase_bills') {
        $status = strtolower(app_normalize_whitespace((string) ($record['status'] ?? '')));
        $remaining = app_purchase_bill_remaining_amount($record);
        if ($remaining > 0 && !in_array($status, ['draft', 'cancelled'], true)) {
            $buttons[] = [
                'label' => 'Register Payment',
                'value' => app_money_format($remaining),
                'href' => 'module.php?module=payments&purchase_bill_id=' . (int) ($record['id'] ?? 0) . '&company_id=' . (int) $companyId,
            ];
        }
    }

    if ($slug === 'expenses') {
        $status = strtolower(app_normalize_whitespace((string) ($record['status'] ?? '')));
        $remaining = app_expense_remaining_amount($record);
        if ($remaining > 0 && !in_array($status, ['draft', 'cancelled'], true)) {
            $buttons[] = [
                'label' => 'Register Payment',
                'value' => app_money_format($remaining),
                'href' => 'module.php?module=payments&expense_id=' . (int) ($record['id'] ?? 0) . '&company_id=' . (int) $companyId,
            ];
        }
    }

    if ($slug === 'student_invoices') {
        $status = strtolower(app_normalize_whitespace((string) ($record['status'] ?? '')));
        $remaining = app_student_invoice_remaining_amount($record);
        if ($remaining > 0 && !in_array($status, ['draft', 'cancelled'], true)) {
            $buttons[] = [
                'label' => 'Student Receipt',
                'value' => app_money_format($remaining),
                'href' => 'module.php?module=student_receipts&student_invoice_id=' . (int) ($record['id'] ?? 0) . '&company_id=' . (int) $companyId,
            ];
        }
    }

    if ($slug === 'journal_entries') {
        $sourceUrl = app_source_module_url(
            app_normalize_whitespace((string) ($record['source_type'] ?? '')),
            (int) ($record['source_id'] ?? 0),
            $companyId
        );
        if ($sourceUrl !== null) {
            $buttons[] = [
                'label' => 'Source',
                'value' => app_source_module_label((string) ($record['source_type'] ?? '')),
                'href' => $sourceUrl,
            ];
        }
    }

    if (!$buttons) {
        return '';
    }

    $html = '<div class="erp-smart-buttons">';
    foreach ($buttons as $button) {
        $html .= '<a class="erp-smart-button" href="' . app_escape((string) $button['href']) . '">';
        $html .= '<span class="erp-smart-value">' . app_escape((string) $button['value']) . '</span>';
        $html .= '<span class="erp-smart-label">' . app_escape((string) $button['label']) . '</span>';
        $html .= '</a>';
    }
    $html .= '</div>';
    return $html;
}

$recordForForm = $editing ?? [];
$userCompanyAccessIds = [];
if ($slug === 'users') {
    $userRecordId = (int) ($recordForForm['id'] ?? 0);
    if ($userRecordId > 0) {
        $userCompanyAccessIds = app_user_company_ids($userRecordId);
    } elseif ($companyId !== null && $companyId > 0) {
        $userCompanyAccessIds = [(int) $companyId];
        $recordForForm['company_id'] = (int) $companyId;
    }

    $primaryCompanyId = (int) ($recordForForm['company_id'] ?? 0);
    if ($primaryCompanyId > 0) {
        $userCompanyAccessIds[] = $primaryCompanyId;
    }
    $userCompanyAccessIds = array_values(array_unique(array_filter($userCompanyAccessIds, static fn (int $id): bool => $id > 0)));
}
if (!$editing && $slug === 'payments' && $companyId !== null && $companyId > 0) {
    $prefillSalesInvoiceId = app_integer($_GET['sales_invoice_id'] ?? 0);
    $prefillSalesInvoice = $prefillSalesInvoiceId > 0 ? app_sales_invoice_by_id((int) $companyId, $prefillSalesInvoiceId) : null;
    if ($prefillSalesInvoice) {
        $paymentOptions = app_module_lookup_options('payment_account_name', (int) $companyId);
        $bankOptions = array_filter(
            $paymentOptions,
            static fn (string $label): bool => str_starts_with($label, 'Bank: ')
        );
        $recordForForm['payment_date'] = date('Y-m-d');
        $recordForForm['direction'] = 'in';
        $recordForForm['payment_method'] = 'transfer';
        $recordForForm['sales_invoice_id'] = (string) $prefillSalesInvoiceId;
        $recordForForm['reference'] = app_normalize_whitespace((string) ($prefillSalesInvoice['invoice_number'] ?? ''));
        $recordForForm['contact_code'] = app_normalize_whitespace((string) ($prefillSalesInvoice['customer_code'] ?? ''));
        $recordForForm['amount'] = app_sales_invoice_remaining_amount($prefillSalesInvoice);
        $recordForForm['bank_account_name'] = $bankOptions ? (string) array_key_first($bankOptions) : ($paymentOptions ? (string) array_key_first($paymentOptions) : '');
    }
    $prefillBillId = app_integer($_GET['purchase_bill_id'] ?? 0);
    $prefillBill = (!$prefillSalesInvoice && $prefillBillId > 0) ? app_purchase_bill_by_id((int) $companyId, $prefillBillId) : null;
    if ($prefillBill) {
        $bankOptions = app_module_lookup_options('payment_account_name', (int) $companyId);
        $recordForForm['payment_date'] = date('Y-m-d');
        $recordForForm['direction'] = 'out';
        $recordForForm['payment_method'] = 'transfer';
        $recordForForm['purchase_bill_id'] = (string) $prefillBillId;
        $recordForForm['reference'] = app_normalize_whitespace((string) ($prefillBill['bill_number'] ?? ''));
        $recordForForm['contact_code'] = app_normalize_whitespace((string) ($prefillBill['vendor_code'] ?? ''));
        $recordForForm['amount'] = app_purchase_bill_remaining_amount($prefillBill);
        $recordForForm['bank_account_name'] = $bankOptions ? (string) array_key_first($bankOptions) : '';
    }
    $prefillExpenseId = app_integer($_GET['expense_id'] ?? 0);
    $prefillExpense = (!$prefillBill && $prefillExpenseId > 0) ? app_expense_by_id((int) $companyId, $prefillExpenseId) : null;
    if ($prefillExpense) {
        $paymentOptions = app_module_lookup_options('payment_account_name', (int) $companyId);
        $recordForForm['payment_date'] = date('Y-m-d');
        $recordForForm['direction'] = 'out';
        $recordForForm['payment_method'] = 'cash';
        $recordForForm['expense_id'] = (string) $prefillExpenseId;
        $recordForForm['reference'] = app_normalize_whitespace((string) ($prefillExpense['expense_number'] ?? ''));
        $recordForForm['contact_code'] = app_normalize_whitespace((string) ($prefillExpense['payee_code'] ?? ''));
        $recordForForm['amount'] = app_expense_remaining_amount($prefillExpense);
        $cashOptions = array_filter(
            $paymentOptions,
            static fn (string $label): bool => str_starts_with($label, 'Cash: ')
        );
        $recordForForm['bank_account_name'] = $cashOptions ? (string) array_key_first($cashOptions) : ($paymentOptions ? (string) array_key_first($paymentOptions) : '');
    }
}
if (!$editing && $slug === 'student_receipts' && $companyId !== null && $companyId > 0) {
    $prefillStudentInvoiceId = app_integer($_GET['student_invoice_id'] ?? 0);
    $prefillStudentInvoice = $prefillStudentInvoiceId > 0 ? app_student_invoice_by_id((int) $companyId, $prefillStudentInvoiceId) : null;
    if ($prefillStudentInvoice) {
        $paymentOptions = app_module_lookup_options('payment_account_name', (int) $companyId);
        $cashOptions = array_filter(
            $paymentOptions,
            static fn (string $label): bool => str_starts_with($label, 'Cash: ')
        );
        $recordForForm['receipt_date'] = date('Y-m-d');
        $recordForForm['student_invoice_id'] = (string) $prefillStudentInvoiceId;
        $recordForForm['student_number'] = app_normalize_whitespace((string) ($prefillStudentInvoice['student_number'] ?? ''));
        $recordForForm['guardian_contact_code'] = app_normalize_whitespace((string) ($prefillStudentInvoice['guardian_contact_code'] ?? ''));
        $recordForForm['reference'] = app_normalize_whitespace((string) ($prefillStudentInvoice['student_invoice_number'] ?? ''));
        $recordForForm['amount'] = app_student_invoice_remaining_amount($prefillStudentInvoice);
        $recordForForm['payment_method'] = 'cash';
        $recordForForm['payment_account_name'] = $cashOptions ? (string) array_key_first($cashOptions) : ($paymentOptions ? (string) array_key_first($paymentOptions) : '');
    }
}
if (!$editing && $slug === 'prepaid_expenses' && $companyId !== null && $companyId > 0) {
    $settings = app_posting_account_settings((int) $companyId);
    $recordForForm['prepaid_date'] = date('Y-m-d');
    $recordForForm['start_date'] = date('Y-m-01');
    $recordForForm['duration_months'] = 12;
    $recordForForm['prepaid_account_code'] = app_normalize_whitespace((string) ($settings['prepaid_expense_account_code'] ?? ''));
    $expenseOptions = app_module_lookup_options('expense_account_code', (int) $companyId);
    $costCenterOptions = app_module_lookup_options('cost_center_code', (int) $companyId);
    if ($expenseOptions) {
        $recordForForm['expense_account_code'] = (string) array_key_first($expenseOptions);
    }
    if ($costCenterOptions) {
        $recordForForm['cost_center_code'] = (string) array_key_first($costCenterOptions);
    }
    $recordForForm['status'] = 'draft';
}
if (!$editing && $slug === 'ifrs16_leases' && $companyId !== null && $companyId > 0) {
    $settings = app_posting_account_settings((int) $companyId);
    $today = new DateTimeImmutable(date('Y-m-d'));
    $costCenterOptions = app_module_lookup_options('cost_center_code', (int) $companyId);
    $recordForForm['country'] = 'Saudi Arabia';
    $recordForForm['currency_code'] = 'SAR';
    $recordForForm['commencement_date'] = $today->format('Y-m-d');
    $recordForForm['end_date'] = $today->modify('+3 years')->modify('-1 day')->format('Y-m-d');
    $recordForForm['first_payment_date'] = app_ifrs16_default_first_payment_date($today->format('Y-m-d'));
    $recordForForm['payment_frequency'] = 'monthly';
    $recordForForm['payment_amount'] = 10000;
    $recordForForm['number_of_payments'] = 36;
    $recordForForm['payments_at_or_before_commencement'] = 0;
    $recordForForm['initial_direct_costs'] = 0;
    $recordForForm['restoration_cost'] = 0;
    $recordForForm['lease_incentives'] = 0;
    $recordForForm['benchmark_rate_percent'] = 4.25;
    $recordForForm['credit_spread_percent'] = 2;
    $recordForForm['manual_discount_rate_percent'] = 0;
    $recordForForm['rou_asset_account_code'] = app_normalize_whitespace((string) ($settings['ifrs16_rou_asset_account_code'] ?? ''));
    $recordForForm['lease_liability_account_code'] = app_normalize_whitespace((string) ($settings['ifrs16_lease_liability_account_code'] ?? ''));
    $recordForForm['interest_expense_account_code'] = app_normalize_whitespace((string) ($settings['ifrs16_interest_expense_account_code'] ?? ''));
    $recordForForm['depreciation_expense_account_code'] = app_normalize_whitespace((string) ($settings['ifrs16_depreciation_expense_account_code'] ?? ''));
    $recordForForm['accumulated_depreciation_account_code'] = app_normalize_whitespace((string) ($settings['ifrs16_accumulated_depreciation_account_code'] ?? ''));
    $recordForForm['restoration_provision_account_code'] = app_normalize_whitespace((string) ($settings['ifrs16_restoration_provision_account_code'] ?? ''));
    $recordForForm['lease_incentive_account_code'] = app_normalize_whitespace((string) ($settings['ifrs16_lease_incentive_account_code'] ?? ''));
    if ($costCenterOptions) {
        $recordForForm['cost_center_code'] = (string) array_key_first($costCenterOptions);
    }
    $recordForForm['status'] = 'draft';
}
$title = (string) ($definition['title'] ?? $slug);
$description = (string) ($definition['description'] ?? '');
$listColumns = (array) ($definition['list_columns'] ?? []);
$chartAccountTreeData = ['rows_by_code' => [], 'children_by_parent' => ['' => []], 'roots' => []];
$chartAccountCodeData = ['rows_by_code' => [], 'children_by_parent' => ['' => []], 'roots' => []];
$chartAccountTypeOptions = [];
$chartStatementGroupOptions = [];
if ($slug === 'chart_of_accounts') {
    $chartAccountTreeData = module_chart_account_tree_data($rows);
    $chartAccountCodeRows = app_module_list('chart_of_accounts', $companyId, '', '', '', []);
    $chartAccountCodeData = module_chart_account_tree_data($chartAccountCodeRows);
    foreach ((array) ($definition['fields'] ?? []) as $field) {
        if ((string) ($field['name'] ?? '') === 'account_type') {
            $chartAccountTypeOptions = app_module_field_options($field);
            break;
        }
    }
    if ($companyId !== null && $companyId > 0) {
        $chartStatementGroupOptions = app_module_lookup_options('financial_statement_group_code', (int) $companyId);
    }
}
$workflow = app_module_workflow_definition($slug);
$recordDisplayName = module_record_display_name($slug, $recordForForm, $title);
$statusbarHtml = $editing ? module_statusbar_html($slug, $recordForForm) : '';
$workflowActionsHtml = $editing ? module_workflow_actions_html($slug, $recordForForm, $companyId) : '';
$smartButtonsHtml = $editing ? module_smart_buttons_html($slug, $recordForForm, $companyId) : '';
$purchaseBudgetSummary = [];
if (in_array($slug, ['purchase_bills', 'expenses'], true) && $companyId !== null && $companyId > 0) {
    $purchaseBudgetSummary = app_budget_summary_for_expense_account(
        (int) $companyId,
        (string) ($recordForForm['expense_account_code'] ?? ''),
        (string) ($recordForForm[$slug === 'expenses' ? 'expense_date' : 'bill_date'] ?? date('Y-m-d'))
    );
}
$depreciationDefaults = [
    'run_date' => date('Y-m-d'),
    'asset_id' => '',
    'depreciation_start_date' => date('Y-m-01'),
    'depreciation_months' => 1,
    'cost_center_code' => '',
    'expense_account_code' => '',
    'accumulated_account_code' => '',
    'notes' => '',
];
$depreciationRunLines = [];
$depreciationCostCenterOptions = [];
$depreciationExpenseOptions = [];
$depreciationAccumulatedOptions = [];
$depreciationAssetOptions = [];
$canRunDepreciation = false;
$prepaidAmortizationDefaults = [
    'prepaid_expense_id' => '',
    'run_date' => date('Y-m-01'),
    'duration_months' => 1,
    'prepaid_account_code' => '',
    'expense_account_code' => '',
    'cost_center_code' => '',
    'notes' => '',
];
$prepaidExpenseOptions = [];
$prepaidAssetOptions = [];
$prepaidExpenseAccountOptions = [];
$prepaidCostCenterOptions = [];
$canRunPrepaidAmortization = false;
$ifrs16ScheduleLines = [];
$ifrs16ScheduleTotals = [
    'payment_amount' => 0.0,
    'interest_expense' => 0.0,
    'depreciation_expense' => 0.0,
];
$ifrs16PaymentRows = [];
$ifrs16PaymentTotals = [
    'payment_amount' => 0.0,
    'pv_at_commencement' => 0.0,
];
$journalDetailEntryId = 0;
$journalDetailEntry = null;
$journalDetailLines = [];
$journalAccountOptions = [];
$journalCostCenterOptions = [];
$journalEditorLines = [];
$journalEntryIsSystemGenerated = false;
$journalViewId = max(0, (int) ($_GET['view'] ?? 0));
$journalPrintRequested = false;
$journalIsViewingOnly = false;
$documentLineEditorEnabled = in_array($slug, app_document_line_slugs(), true) && $companyId !== null && $companyId > 0;
$documentLineRows = [];
$documentLineAccountOptions = [];
$documentLineCostCenterOptions = [];
$documentLineTaxOptions = [];
$documentLineItemOptions = [];
$documentLineItemLabel = 'Item';
$documentLineAccountLabel = 'Account';
$invoicePrintRequested = $documentLineEditorEnabled && $editing && app_normalize_whitespace((string) ($_GET['print'] ?? '')) === '1';
$roleMatrixRoles = [];
$roleMatrixSelectedRole = '';
$roleMatrixState = ['allowed_modules' => [], 'can_edit_all' => false, 'can_post_all' => false];
$roleMatrixAllowedMap = [];
$roleMatrixGroups = [];
if ($slug === 'role_permissions') {
    $roleMatrixRoles = app_roles_list(true);
    $requestedRole = app_current_role_normalize((string) ($_GET['role_code'] ?? ($_POST['role_code'] ?? '')));
    if ($requestedRole === '' && $roleMatrixRoles) {
        $requestedRole = app_current_role_normalize((string) ($roleMatrixRoles[0]['role_code'] ?? ''));
    }
    $roleMatrixSelectedRole = $requestedRole;
    $roleMatrixState = app_role_permission_simple_state($roleMatrixSelectedRole);
    $roleMatrixAllowedMap = array_fill_keys((array) ($roleMatrixState['allowed_modules'] ?? []), true);
    $allDefinitions = app_module_definitions();
    $groupSpecs = [
        'General Accounting' => app_accounting_workspace_module_slugs(),
        'Student Accounts' => app_student_workspace_module_slugs(),
        'EXcellence Accounts' => app_excellence_workspace_module_slugs(),
        'Human Resources' => app_hr_workspace_module_slugs(),
    ];
    foreach ($groupSpecs as $groupName => $groupSlugs) {
        $items = [];
        foreach ($groupSlugs as $groupSlug) {
            if (!isset($allDefinitions[$groupSlug])) {
                continue;
            }
            $localized = app_localize_module_definition($allDefinitions[$groupSlug]);
            $items[] = [
                'slug' => $groupSlug,
                'title' => (string) ($localized['title'] ?? $groupSlug),
                'description' => (string) ($localized['description'] ?? ''),
                'icon' => (string) ($localized['icon'] ?? strtoupper(substr($groupSlug, 0, 2))),
            ];
        }
        if ($items) {
            $roleMatrixGroups[$groupName] = $items;
        }
    }
}
if ($slug === 'journal_entries' && $companyId !== null && $companyId > 0) {
    $journalViewEntry = (!$editing && $journalViewId > 0)
        ? app_module_find('journal_entries', $journalViewId, $companyId)
        : null;
    $journalDetailEntry = $editing ?: $journalViewEntry;
    $journalDetailEntryId = (int) ($journalDetailEntry['id'] ?? 0);
    if ($journalDetailEntryId > 0) {
        $journalDetailLines = app_journal_entry_lines((int) $companyId, $journalDetailEntryId);
    }
    $journalPrintRequested = $journalDetailEntryId > 0 && app_normalize_whitespace((string) ($_GET['print'] ?? '')) === '1';
    $journalIsViewingOnly = !$editing && $journalDetailEntryId > 0;
    if ($journalIsViewingOnly && $journalDetailEntry) {
        $recordDisplayName = module_record_display_name($slug, $journalDetailEntry, $title);
    }

    $journalAccountOptions = app_module_lookup_options('chart_account_code', (int) $companyId);
    $journalCostCenterOptions = app_module_lookup_options('cost_center_code', (int) $companyId);
    if (!$editing) {
        $recordForForm['entry_number'] = '';
        $recordForForm['entry_date'] = date('Y-m-d');
        $recordForForm['reference'] = '';
        $recordForForm['description'] = '';
        $recordForForm['status'] = 'draft';
    }
    $journalEntryIsSystemGenerated = (int) ($recordForForm['is_system_generated'] ?? 0) === 1;
    $journalEditorLines = $editing ? $journalDetailLines : [];
    while (count($journalEditorLines) < 2) {
        $journalEditorLines[] = [
            'account_code' => '',
            'cost_center_code' => '',
            'description' => '',
            'debit' => '',
            'credit' => '',
        ];
    }
}

if ($documentLineEditorEnabled && $companyId !== null && $companyId > 0) {
    $lineConfig = app_document_line_config($slug);
    $documentLineAccountOptions = app_module_lookup_options((string) ($lineConfig['account_type'] ?? '') . '_account_code', (int) $companyId);
    $documentLineCostCenterOptions = app_module_lookup_options('cost_center_code', (int) $companyId);
    $documentLineTaxOptions = app_module_lookup_options(in_array($slug, ['purchase_bills', 'expenses', 'purchase_returns', 'expense_returns'], true) ? 'purchase_tax_name' : 'sales_tax_name', (int) $companyId);
    if (in_array($slug, ['sales_invoices', 'purchase_bills', 'sales_returns', 'purchase_returns'], true)) {
        $documentLineItemOptions = app_module_lookup_options('product_sku', (int) $companyId);
        $documentLineItemLabel = 'Product';
    } elseif ($slug === 'student_invoices') {
        $documentLineItemOptions = app_module_lookup_options('student_revenue_type_code', (int) $companyId);
        $documentLineItemLabel = 'Revenue Type';
    } else {
        $documentLineItemLabel = 'Category';
    }
    $documentLineAccountLabel = in_array($slug, ['sales_invoices', 'student_invoices'], true) ? 'Revenue Account' : 'Expense Account';
    $documentId = (int) ($recordForForm['id'] ?? 0);
    $documentLineRows = $documentId > 0
        ? app_fetch_document_lines($slug, (int) $companyId, $documentId, $recordForForm)
        : app_document_line_from_record($slug, $recordForForm);
    if (!$documentLineRows) {
        $documentLineRows[] = [
            'item_code' => $slug === 'student_invoices' ? app_normalize_whitespace((string) ($recordForForm['revenue_type_code'] ?? '')) : '',
            'description' => app_normalize_whitespace((string) ($recordForForm['description'] ?? '')),
            'account_code' => app_normalize_whitespace((string) ($recordForForm[in_array($slug, ['sales_invoices', 'student_invoices'], true) ? 'revenue_account_code' : 'expense_account_code'] ?? '')),
            'cost_center_code' => app_normalize_whitespace((string) ($recordForForm['cost_center_code'] ?? '')),
            'quantity' => 1,
            'unit_price' => app_number($recordForForm['subtotal'] ?? 0),
            'discount_total' => app_number($recordForForm['discount_total'] ?? 0),
            'is_taxable' => !empty($recordForForm['is_taxable']) ? 1 : 0,
            'tax_name' => app_normalize_whitespace((string) ($recordForForm['tax_name'] ?? '')),
            'tax_rate_percent' => app_number($recordForForm['tax_rate_percent'] ?? 0),
            'subtotal' => app_number($recordForForm['subtotal'] ?? 0),
            'tax_total' => app_number($recordForForm['tax_total'] ?? 0),
            'line_total' => app_number($recordForForm['grand_total'] ?? 0),
        ];
    }
}

if ($slug === 'depreciation_runs' && $companyId !== null && $companyId > 0) {
    $depreciationDefaults['run_date'] = (string) ($_GET['run_date'] ?? $depreciationDefaults['run_date']);
    $depreciationDefaults['asset_id'] = app_normalize_whitespace((string) ($_GET['asset_id'] ?? ''));
    $depreciationDefaults['depreciation_start_date'] = (string) ($_GET['depreciation_start_date'] ?? $depreciationDefaults['depreciation_start_date']);
    $depreciationDefaults['depreciation_months'] = max(1, app_integer($_GET['depreciation_months'] ?? $depreciationDefaults['depreciation_months']));
    $depreciationDefaults['cost_center_code'] = app_normalize_whitespace((string) ($_GET['cost_center_code'] ?? ''));
    $depreciationDefaults['expense_account_code'] = app_normalize_whitespace((string) ($_GET['expense_account_code'] ?? ''));
    $depreciationDefaults['accumulated_account_code'] = app_normalize_whitespace((string) ($_GET['accumulated_account_code'] ?? ''));
    $depreciationDefaults['notes'] = app_normalize_whitespace((string) ($_GET['notes'] ?? ''));

    if ($editing) {
        $depreciationDefaults['run_date'] = (string) ($editing['run_date'] ?? $depreciationDefaults['run_date']);
        $depreciationDefaults['asset_id'] = app_normalize_whitespace((string) ($editing['asset_id'] ?? $depreciationDefaults['asset_id']));
        $depreciationDefaults['depreciation_start_date'] = (string) ($editing['schedule_start_date'] ?? $depreciationDefaults['depreciation_start_date']);
        $depreciationDefaults['depreciation_months'] = max(1, app_integer($editing['duration_months'] ?? $depreciationDefaults['depreciation_months']));
        $depreciationDefaults['cost_center_code'] = app_normalize_whitespace((string) ($editing['cost_center_code'] ?? $depreciationDefaults['cost_center_code']));
        $depreciationDefaults['expense_account_code'] = app_normalize_whitespace((string) ($editing['expense_account_code'] ?? $depreciationDefaults['expense_account_code']));
        $depreciationDefaults['accumulated_account_code'] = app_normalize_whitespace((string) ($editing['accumulated_account_code'] ?? $depreciationDefaults['accumulated_account_code']));
        $depreciationDefaults['notes'] = app_normalize_whitespace((string) ($editing['notes'] ?? $depreciationDefaults['notes']));
    }

    $depreciationCostCenterOptions = app_module_lookup_options('cost_center_code', $companyId);
    $depreciationExpenseOptions = app_module_lookup_options('expense_account_code', $companyId);
    $depreciationAccumulatedOptions = app_module_lookup_options('asset_account_code', $companyId);
    $depreciationAssetOptions = app_module_lookup_options('asset_id', $companyId);
    $canRunDepreciation = !empty($depreciationCostCenterOptions) && !empty($depreciationExpenseOptions) && !empty($depreciationAccumulatedOptions);

    if ($depreciationDefaults['cost_center_code'] === '' && $depreciationCostCenterOptions) {
        $depreciationDefaults['cost_center_code'] = (string) array_key_first($depreciationCostCenterOptions);
    }
    if ($depreciationDefaults['expense_account_code'] === '' && $depreciationExpenseOptions) {
        $depreciationDefaults['expense_account_code'] = (string) array_key_first($depreciationExpenseOptions);
    }
    if ($depreciationDefaults['accumulated_account_code'] === '' && $depreciationAccumulatedOptions) {
        $depreciationDefaults['accumulated_account_code'] = (string) array_key_first($depreciationAccumulatedOptions);
    }

    if ($editId > 0) {
        $depreciationRunLines = app_depreciation_run_lines((int) $companyId, $editId);
    }
}

if ($slug === 'prepaid_amortization_runs' && $companyId !== null && $companyId > 0) {
    $prepaidAmortizationDefaults['prepaid_expense_id'] = app_normalize_whitespace((string) ($_GET['prepaid_expense_id'] ?? $prepaidAmortizationDefaults['prepaid_expense_id']));
    $prepaidAmortizationDefaults['run_date'] = (string) ($_GET['run_date'] ?? $prepaidAmortizationDefaults['run_date']);
    $prepaidAmortizationDefaults['duration_months'] = max(1, app_integer($_GET['duration_months'] ?? $prepaidAmortizationDefaults['duration_months']));
    $prepaidAmortizationDefaults['prepaid_account_code'] = app_normalize_whitespace((string) ($_GET['prepaid_account_code'] ?? ''));
    $prepaidAmortizationDefaults['expense_account_code'] = app_normalize_whitespace((string) ($_GET['expense_account_code'] ?? ''));
    $prepaidAmortizationDefaults['cost_center_code'] = app_normalize_whitespace((string) ($_GET['cost_center_code'] ?? ''));
    $prepaidAmortizationDefaults['notes'] = app_normalize_whitespace((string) ($_GET['notes'] ?? ''));

    if ($editing) {
        $prepaidAmortizationDefaults['prepaid_expense_id'] = app_normalize_whitespace((string) ($editing['prepaid_expense_id'] ?? $prepaidAmortizationDefaults['prepaid_expense_id']));
        $prepaidAmortizationDefaults['run_date'] = (string) ($editing['run_date'] ?? $prepaidAmortizationDefaults['run_date']);
        $prepaidAmortizationDefaults['prepaid_account_code'] = app_normalize_whitespace((string) ($editing['prepaid_account_code'] ?? $prepaidAmortizationDefaults['prepaid_account_code']));
        $prepaidAmortizationDefaults['expense_account_code'] = app_normalize_whitespace((string) ($editing['expense_account_code'] ?? $prepaidAmortizationDefaults['expense_account_code']));
        $prepaidAmortizationDefaults['cost_center_code'] = app_normalize_whitespace((string) ($editing['cost_center_code'] ?? $prepaidAmortizationDefaults['cost_center_code']));
        $prepaidAmortizationDefaults['notes'] = app_normalize_whitespace((string) ($editing['notes'] ?? $prepaidAmortizationDefaults['notes']));
    }

    $prepaidExpenseOptions = app_module_lookup_options('prepaid_expense_id', $companyId);
    $prepaidAssetOptions = app_module_lookup_options('asset_account_code', $companyId);
    $prepaidExpenseAccountOptions = app_module_lookup_options('expense_account_code', $companyId);
    $prepaidCostCenterOptions = app_module_lookup_options('cost_center_code', $companyId);

    $selectedPrepaid = app_integer($prepaidAmortizationDefaults['prepaid_expense_id'] ?? 0) > 0
        ? app_prepaid_expense_by_id((int) $companyId, app_integer($prepaidAmortizationDefaults['prepaid_expense_id']))
        : null;
    if ($selectedPrepaid) {
        if ($prepaidAmortizationDefaults['prepaid_account_code'] === '') {
            $prepaidAmortizationDefaults['prepaid_account_code'] = app_normalize_whitespace((string) ($selectedPrepaid['prepaid_account_code'] ?? ''));
        }
        if ($prepaidAmortizationDefaults['expense_account_code'] === '') {
            $prepaidAmortizationDefaults['expense_account_code'] = app_normalize_whitespace((string) ($selectedPrepaid['expense_account_code'] ?? ''));
        }
        if ($prepaidAmortizationDefaults['cost_center_code'] === '') {
            $prepaidAmortizationDefaults['cost_center_code'] = app_normalize_whitespace((string) ($selectedPrepaid['cost_center_code'] ?? ''));
        }
    }
    if ($prepaidAmortizationDefaults['prepaid_account_code'] === '' && $prepaidAssetOptions) {
        $prepaidAmortizationDefaults['prepaid_account_code'] = (string) array_key_first($prepaidAssetOptions);
    }
    if ($prepaidAmortizationDefaults['expense_account_code'] === '' && $prepaidExpenseAccountOptions) {
        $prepaidAmortizationDefaults['expense_account_code'] = (string) array_key_first($prepaidExpenseAccountOptions);
    }
    if ($prepaidAmortizationDefaults['cost_center_code'] === '' && $prepaidCostCenterOptions) {
        $prepaidAmortizationDefaults['cost_center_code'] = (string) array_key_first($prepaidCostCenterOptions);
    }
    $canRunPrepaidAmortization = !empty($prepaidExpenseOptions)
        && !empty($prepaidAssetOptions)
        && !empty($prepaidExpenseAccountOptions)
        && !empty($prepaidCostCenterOptions);
}

if ($slug === 'ifrs16_leases' && $editing && $companyId !== null && $companyId > 0 && $editId > 0) {
    $ifrs16PaymentRows = app_ifrs16_lease_payment_rows_for_calculation((int) $companyId, $editId, $recordForForm);
    foreach ($ifrs16PaymentRows as $paymentRow) {
        $ifrs16PaymentTotals['payment_amount'] += app_number($paymentRow['payment_amount'] ?? 0);
        $ifrs16PaymentTotals['pv_at_commencement'] += app_number($paymentRow['pv_at_commencement'] ?? 0);
    }
    $ifrs16ScheduleLines = app_ifrs16_lease_schedule_lines((int) $companyId, $editId);
    foreach ($ifrs16ScheduleLines as $scheduleLine) {
        $ifrs16ScheduleTotals['payment_amount'] += app_number($scheduleLine['payment_amount'] ?? 0);
        $ifrs16ScheduleTotals['interest_expense'] += app_number($scheduleLine['interest_expense'] ?? 0);
        $ifrs16ScheduleTotals['depreciation_expense'] += app_number($scheduleLine['depreciation_expense'] ?? 0);
    }
}

if ($slug === 'journal_entries' && app_normalize_whitespace((string) ($_GET['export'] ?? '')) === 'xlsx') {
    $companyName = (string) ($company['name'] ?? 'N/A');
    $preparedBy = (string) (app_current_user()['full_name'] ?? app_current_username());
    $infoRows = static function (string $title) use ($companyName, $preparedBy): array {
        return [
            app_xlsx_row([$title], 'title'),
            ['Company', $companyName],
            ['Prepared By', $preparedBy],
            ['Generated At', date('Y-m-d H:i')],
            [],
        ];
    };

    if ($journalDetailEntryId > 0 && $journalDetailEntry && $journalDetailLines) {
        $totalDebit = 0.0;
        $totalCredit = 0.0;
        foreach ($journalDetailLines as $line) {
            $totalDebit += app_number($line['debit'] ?? 0);
            $totalCredit += app_number($line['credit'] ?? 0);
        }
        $sourceType = app_normalize_whitespace((string) ($journalDetailEntry['source_type'] ?? ''));
        $entryRows = array_merge($infoRows('Journal Entry'), [
            ['Entry Number', (string) ($journalDetailEntry['entry_number'] ?? '')],
            ['Entry Date', (string) ($journalDetailEntry['entry_date'] ?? '')],
            ['Reference', (string) ($journalDetailEntry['reference'] ?? '')],
            ['Status', (string) ($journalDetailEntry['status'] ?? '')],
            ['Source', app_source_module_label($sourceType)],
            ['Description', (string) ($journalDetailEntry['description'] ?? '')],
            [],
            app_xlsx_row(['Line', 'Account Code', 'Account Name', 'Cost Center', 'Description', 'Debit', 'Credit'], 'header'),
        ]);
        foreach ($journalDetailLines as $line) {
            $center = app_normalize_whitespace((string) ($line['cost_center_code'] ?? ''));
            $centerName = app_normalize_whitespace((string) ($line['cost_center_name'] ?? ''));
            if ($centerName !== '' && $centerName !== $center) {
                $center .= ' - ' . $centerName;
            }
            $entryRows[] = [
                (int) ($line['line_no'] ?? 0),
                (string) ($line['account_code'] ?? ''),
                (string) ($line['account_name'] ?? ''),
                $center,
                (string) ($line['description'] ?? ''),
                app_number($line['debit'] ?? 0),
                app_number($line['credit'] ?? 0),
            ];
        }
        $entryRows[] = app_xlsx_row(['Totals', '', '', '', '', $totalDebit, $totalCredit], 'total');
        $entryRows[] = app_xlsx_row(['Difference', '', '', '', '', round($totalDebit - $totalCredit, 2), ''], 'total');
        app_export_xlsx('journal-entry-' . (string) ($journalDetailEntry['entry_number'] ?? $journalDetailEntryId) . '.xlsx', [
            ['title' => 'Journal Entry', 'rows' => $entryRows],
        ]);
    }

    $listRows = array_merge($infoRows('Journal Entries'), [
        ['Search', $q],
        ['From Date', $filterFromDate],
        ['To Date', $filterToDate],
        [],
        app_xlsx_row(['Entry Number', 'Entry Date', 'Reference', 'Source Type', 'Source ID', 'Lines', 'Lines Debit', 'Lines Credit', 'Total Debit', 'Total Credit', 'Status'], 'header'),
    ]);
    foreach ($rows as $row) {
        $listRows[] = [
            (string) ($row['entry_number'] ?? ''),
            (string) ($row['entry_date'] ?? ''),
            (string) ($row['reference'] ?? ''),
            (string) ($row['source_type'] ?? ''),
            (string) ($row['source_id'] ?? ''),
            (int) ($row['line_items_count'] ?? 0),
            app_number($row['line_items_total_debit'] ?? 0),
            app_number($row['line_items_total_credit'] ?? 0),
            app_number($row['total_debit'] ?? 0),
            app_number($row['total_credit'] ?? 0),
            (string) ($row['status'] ?? ''),
        ];
    }
    app_export_xlsx('journal-entries-' . date('Ymd-His') . '.xlsx', [
        ['title' => 'Journal Entries', 'rows' => $listRows],
    ]);
}
?>
<!doctype html>
<html <?= app_html_attrs() ?>>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title><?= app_escape(app_t((string) app_config('app_name'))) ?> - <?= app_escape($title) ?></title>
  <link rel="icon" href="assets/favicon.svg" type="image/svg+xml">
  <link rel="shortcut icon" href="assets/favicon.svg" type="image/svg+xml">
  <link rel="stylesheet" href="assets/styles.css">
</head>
<body>
<div class="app-shell has-sidebar">
  <?= app_sidebar_nav_html('module', $slug, $companyId) ?>

  <header class="header">
    <div class="header-side right">
      <div class="header-title"><?= app_escape($title) ?></div>
      <div class="header-subtitle"><?= app_escape($description) ?></div>
    </div>
    <div class="header-center">
      <img src="<?= app_escape(app_logo_path_for_header($company)) ?>" alt="Logo" class="school-logo">
    </div>
    <div class="header-side left">
      <div class="header-title"><?= app_escape((string) (app_current_user()['full_name'] ?? app_current_username())) ?></div>
      <div class="header-subtitle"><a href="index.php<?= $companyId ? '?company_id=' . (int) $companyId : '' ?>"><?= app_escape(app_t('Dashboard')) ?></a> &bull; <a href="logout.php"><?= app_escape(app_t('Logout')) ?></a></div>
    </div>
  </header>

  <div class="date-bar">
    <div class="date-item"><?= app_escape(app_t('Date :')) ?> <span><?= app_escape(date('Y-m-d')) ?></span></div>
    <div class="date-item"><?= app_escape(app_t('Module :')) ?> <span><?= app_escape($title) ?></span></div>
    <div class="date-author"><?= app_escape(app_t('Prepared by :')) ?> <?= app_escape((string) (app_current_user()['full_name'] ?? app_current_username())) ?></div>
  </div>

  <?= app_back_button_html('Back') ?>

  <div class="container">
    <div class="flash-stack">
      <?php if ($success = app_flash('success')): ?>
        <div class="alert success"><?= app_escape($success) ?></div>
      <?php endif; ?>
      <?php if ($error = app_flash('error')): ?>
        <div class="alert error"><?= app_escape($error) ?></div>
      <?php endif; ?>
    </div>

    <section class="erp-control-panel screen-only">
      <div class="erp-breadcrumbs">
        <a href="index.php<?= $companyId ? '?company_id=' . (int) $companyId : '' ?>"><?= app_escape(app_t('Dashboard')) ?></a>
        <span>/</span>
        <strong><?= app_escape($title) ?></strong>
      </div>
      <div class="erp-control-main">
        <div>
          <h1><?= app_escape($recordDisplayName) ?></h1>
          <p><?= app_escape($description) ?></p>
        </div>
        <form method="get" class="erp-search">
          <input type="hidden" name="module" value="<?= app_escape($slug) ?>">
          <?php if ($companyId !== null): ?>
            <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
          <?php endif; ?>
          <input type="search" name="q" value="<?= app_escape($q) ?>" placeholder="Search <?= app_escape($title) ?>">
          <?php if ($dateFilterColumn !== ''): ?>
            <input type="date" name="from_date" value="<?= app_escape($filterFromDate) ?>" title="From date">
            <input type="date" name="to_date" value="<?= app_escape($filterToDate) ?>" title="To date">
          <?php endif; ?>
          <?php if (in_array($slug, ['sales_invoices', 'student_invoices', 'purchase_bills'], true)): ?>
            <select name="contact_filter" title="<?= $slug === 'purchase_bills' ? 'Vendor' : 'Customer' ?>">
              <option value=""><?= $slug === 'purchase_bills' ? 'All Vendors' : 'All Customers' ?></option>
              <?php foreach ($invoiceContactOptions as $contactCode => $contactLabel): ?>
                <option value="<?= app_escape((string) $contactCode) ?>" <?= $invoiceContactFilter === (string) $contactCode ? 'selected' : '' ?>>
                  <?= app_escape((string) $contactLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          <?php endif; ?>
          <button class="btn btn-secondary" type="submit">Search</button>
          <?php if ($q !== '' || $filterFromDate !== '' || $filterToDate !== '' || $invoiceContactFilter !== ''): ?>
            <a class="btn btn-ghost" href="<?= app_escape($moduleUrl()) ?>">Clear</a>
          <?php endif; ?>
        </form>
      </div>
      <?php if ($editing && ($workflowActionsHtml !== '' || $statusbarHtml !== '' || $smartButtonsHtml !== '')): ?>
        <div class="erp-document-toolbar">
          <div class="erp-document-actions"><?= $workflowActionsHtml ?></div>
          <?= $statusbarHtml ?>
          <?= $smartButtonsHtml ?>
        </div>
      <?php endif; ?>
    </section>

    <?php if (in_array($slug, ['purchase_bills', 'expenses'], true)): ?>
    <section class="erp-budget-strip screen-only">
      <div class="erp-budget-tile">
        <span>Fiscal Year</span>
        <strong><?= app_escape((string) ($purchaseBudgetSummary['fiscal_year'] ?? date('Y'))) ?></strong>
      </div>
      <div class="erp-budget-tile">
        <span>Expense Account</span>
        <strong><?= app_escape((string) ($purchaseBudgetSummary['account_code'] ?? 'N/A')) ?></strong>
      </div>
      <div class="erp-budget-tile">
        <span>Budget</span>
        <strong><?= app_escape(app_money_format($purchaseBudgetSummary['budget_amount'] ?? 0)) ?></strong>
      </div>
      <div class="erp-budget-tile">
        <span>Spent</span>
        <strong><?= app_escape(app_money_format($purchaseBudgetSummary['spent_amount'] ?? 0)) ?></strong>
      </div>
      <div class="erp-budget-tile <?= app_number($purchaseBudgetSummary['remaining_amount'] ?? 0) < 0 ? 'is-over' : 'is-ok' ?>">
        <span>Remaining</span>
        <strong><?= app_escape(app_money_format($purchaseBudgetSummary['remaining_amount'] ?? 0)) ?></strong>
      </div>
    </section>
    <?php endif; ?>

    <?php if ($slug === 'role_permissions'): ?>
    <section class="panel screen-only">
      <div class="erp-form-head">
        <div>
          <h3><?= app_escape(app_t('Simple Role Permissions')) ?></h3>
          <span><?= app_escape(app_t('Choose the modules this role can open, then enable global edit/import/delete and posting permissions.')) ?></span>
        </div>
      </div>

      <form method="get" class="erp-search" style="margin-bottom:18px;">
        <input type="hidden" name="module" value="role_permissions">
        <label class="field" style="margin:0;min-width:280px;">
          <span><?= app_escape(app_t('Role')) ?></span>
          <select name="role_code" onchange="this.form.submit()">
            <?php foreach ($roleMatrixRoles as $role): ?>
              <?php $roleCode = app_current_role_normalize((string) ($role['role_code'] ?? '')); ?>
              <option value="<?= app_escape($roleCode) ?>" <?= $roleMatrixSelectedRole === $roleCode ? 'selected' : '' ?>>
                <?= app_escape(app_ui_language() === 'ar' ? app_t((string) ($role['role_name'] ?? $roleCode)) : ($roleCode . ' - ' . (string) ($role['role_name'] ?? ''))) ?>
              </option>
            <?php endforeach; ?>
          </select>
        </label>
        <button class="btn btn-secondary" type="submit"><?= app_escape(app_t('Load')) ?></button>
      </form>

      <?php if (!$roleMatrixRoles || $roleMatrixSelectedRole === ''): ?>
        <div class="empty-state"><?= app_escape(app_t('Create an active role first.')) ?></div>
      <?php else: ?>
        <form method="post" class="stacked">
          <?= app_csrf_field() ?>
          <input type="hidden" name="action" value="save_role_permissions_matrix">
          <input type="hidden" name="role_code" value="<?= app_escape($roleMatrixSelectedRole) ?>">

          <div class="workspace-kpis" style="margin-bottom:18px;">
            <label class="workspace-kpi" style="cursor:pointer;">
              <span><?= app_escape(app_t('Can Edit All Selected Modules')) ?></span>
              <strong><input type="checkbox" name="can_edit_all" value="1" <?= !empty($roleMatrixState['can_edit_all']) ? 'checked' : '' ?>></strong>
            </label>
            <label class="workspace-kpi" style="cursor:pointer;">
              <span><?= app_escape(app_t('Can Post All Selected Modules')) ?></span>
              <strong><input type="checkbox" name="can_post_all" value="1" <?= !empty($roleMatrixState['can_post_all']) ? 'checked' : '' ?>></strong>
            </label>
          </div>

          <?php foreach ($roleMatrixGroups as $groupName => $items): ?>
            <div class="workspace-section" style="margin-top:18px;">
              <div class="workspace-section-head">
                <h2><?= app_escape(app_t((string) $groupName)) ?></h2>
                <span><?= app_escape(number_format(count((array) $items))) ?> <?= app_escape(app_t('modules')) ?></span>
              </div>
              <div class="workspace-card-grid">
                <?php foreach ((array) $items as $item): ?>
                  <?php $itemSlug = (string) ($item['slug'] ?? ''); ?>
                  <label class="workspace-card" style="--workspace-accent:#0f766e; cursor:pointer;">
                    <span class="workspace-card-icon"><?= app_escape((string) ($item['icon'] ?? 'MD')) ?></span>
                    <strong><?= app_escape((string) ($item['title'] ?? $itemSlug)) ?></strong>
                    <small><?= app_escape((string) ($item['description'] ?? '')) ?></small>
                    <span class="field-help">
                      <input type="checkbox" name="allowed_modules[]" value="<?= app_escape($itemSlug) ?>" <?= isset($roleMatrixAllowedMap[$itemSlug]) ? 'checked' : '' ?>>
                      <?= app_escape(app_t('Can Open')) ?>
                    </span>
                  </label>
                <?php endforeach; ?>
              </div>
            </div>
          <?php endforeach; ?>

          <div class="form-actions">
            <button class="btn btn-primary" type="submit"><?= app_escape(app_t('Save Permissions')) ?></button>
            <a class="btn btn-secondary" href="<?= app_escape($moduleUrl()) ?>"><?= app_escape(app_t('Reset')) ?></a>
          </div>
        </form>
      <?php endif; ?>
    </section>

    <?php elseif ($slug === 'depreciation_runs'): ?>
    <section class="panel screen-only">
      <div class="section-heading">
        <h3>Run Automatic Depreciation</h3>
        <span>Create monthly depreciation entries as draft journal lines for review.</span>
      </div>
      <?php if (!$canRunDepreciation): ?>
        <div class="alert error">Cannot run depreciation yet. Please ensure Cost Centers, Expense accounts, and Asset accounts are configured.</div>
      <?php endif; ?>
      <form method="post" class="stacked">
        <?= app_csrf_field() ?>
        <input type="hidden" name="action" value="run_depreciation">
        <?php if ($companyId !== null): ?>
          <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
        <?php endif; ?>

        <div class="form-grid two">
          <div class="field">
            <label for="asset_id">Asset to Depreciate</label>
            <select id="asset_id" name="asset_id">
              <option value="">All active assets</option>
              <?php foreach ($depreciationAssetOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($depreciationDefaults['asset_id'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field">
            <label for="run_date">Run Date</label>
            <input id="run_date" name="run_date" type="date" value="<?= app_escape((string) ($depreciationDefaults['run_date'] ?? date('Y-m-d'))) ?>" required>
          </div>

          <div class="field">
            <label for="depreciation_start_date">Depreciation Start Date</label>
            <input id="depreciation_start_date" name="depreciation_start_date" type="date" value="<?= app_escape((string) ($depreciationDefaults['depreciation_start_date'] ?? date('Y-m-01'))) ?>">
          </div>

          <div class="field">
            <label for="depreciation_months">Depreciation Duration (Months)</label>
            <input id="depreciation_months" name="depreciation_months" type="number" step="1" min="1" value="<?= app_escape((string) ($depreciationDefaults['depreciation_months'] ?? 1)) ?>">
          </div>

          <div class="field">
            <label for="cost_center_code">Cost Center</label>
            <select id="cost_center_code" name="cost_center_code" required>
              <?php if (!$depreciationCostCenterOptions): ?>
                <option value="">No active cost centers</option>
              <?php endif; ?>
              <?php foreach ($depreciationCostCenterOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($depreciationDefaults['cost_center_code'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field">
            <label for="expense_account_code">Expense Account</label>
            <select id="expense_account_code" name="expense_account_code" required>
              <?php if (!$depreciationExpenseOptions): ?>
                <option value="">No expense accounts</option>
              <?php endif; ?>
              <?php foreach ($depreciationExpenseOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($depreciationDefaults['expense_account_code'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field">
            <label for="accumulated_account_code">Accumulated Depreciation Account</label>
            <select id="accumulated_account_code" name="accumulated_account_code" required>
              <?php if (!$depreciationAccumulatedOptions): ?>
                <option value="">No asset accounts</option>
              <?php endif; ?>
              <?php foreach ($depreciationAccumulatedOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($depreciationDefaults['accumulated_account_code'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field" style="grid-column: 1 / -1;">
            <label for="notes">Notes</label>
            <textarea id="notes" name="notes"><?= app_escape((string) ($depreciationDefaults['notes'] ?? '')) ?></textarea>
          </div>
        </div>

        <div class="form-actions">
          <button class="btn btn-primary" type="submit"<?= ($canRunDepreciation && $canCreate) ? '' : ' disabled' ?>>Run Depreciation</button>
          <?php if ($editing): ?>
            <a class="btn btn-secondary" href="<?= app_escape($moduleUrl()) ?>">Clear Selection</a>
          <?php endif; ?>
        </div>
      </form>
    </section>
    <?php elseif ($slug === 'prepaid_amortization_runs'): ?>
    <section class="panel screen-only">
      <div class="section-heading">
        <h3>Run Prepaid Amortization</h3>
        <span>Create draft monthly entries: debit expense and credit prepaid asset.</span>
      </div>
      <?php if (!$canRunPrepaidAmortization): ?>
        <div class="alert error">Cannot run prepaid amortization yet. Please configure prepaid expenses, accounts, and cost centers.</div>
      <?php endif; ?>
      <form method="post" class="stacked">
        <?= app_csrf_field() ?>
        <input type="hidden" name="action" value="run_prepaid_amortization">
        <?php if ($companyId !== null): ?>
          <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
        <?php endif; ?>

        <div class="form-grid two">
          <div class="field">
            <label for="prepaid_expense_id">Prepaid Expense</label>
            <select id="prepaid_expense_id" name="prepaid_expense_id" required>
              <?php if (!$prepaidExpenseOptions): ?>
                <option value="">No active prepaid expenses</option>
              <?php endif; ?>
              <?php foreach ($prepaidExpenseOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($prepaidAmortizationDefaults['prepaid_expense_id'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field">
            <label for="prepaid_run_date">Start Period</label>
            <input id="prepaid_run_date" name="run_date" type="date" value="<?= app_escape((string) ($prepaidAmortizationDefaults['run_date'] ?? date('Y-m-01'))) ?>" required>
          </div>

          <div class="field">
            <label for="prepaid_duration_months">Number of Months to Generate</label>
            <input id="prepaid_duration_months" name="duration_months" type="number" step="1" min="1" max="360" value="<?= app_escape((string) ($prepaidAmortizationDefaults['duration_months'] ?? 1)) ?>" required>
          </div>

          <div class="field">
            <label for="prepaid_account_code">Prepaid Account</label>
            <select id="prepaid_account_code" name="prepaid_account_code" required>
              <?php foreach ($prepaidAssetOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($prepaidAmortizationDefaults['prepaid_account_code'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field">
            <label for="prepaid_expense_account_code">Expense Account</label>
            <select id="prepaid_expense_account_code" name="expense_account_code" required>
              <?php foreach ($prepaidExpenseAccountOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($prepaidAmortizationDefaults['expense_account_code'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field">
            <label for="prepaid_cost_center_code">Cost Center</label>
            <select id="prepaid_cost_center_code" name="cost_center_code" required>
              <?php foreach ($prepaidCostCenterOptions as $optionValue => $optionLabel): ?>
                <option value="<?= app_escape((string) $optionValue) ?>" <?= (string) ($prepaidAmortizationDefaults['cost_center_code'] ?? '') === (string) $optionValue ? 'selected' : '' ?>>
                  <?= app_escape((string) $optionLabel) ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div class="field" style="grid-column: 1 / -1;">
            <label for="prepaid_notes">Notes</label>
            <textarea id="prepaid_notes" name="notes"><?= app_escape((string) ($prepaidAmortizationDefaults['notes'] ?? '')) ?></textarea>
          </div>
        </div>

        <div class="form-actions">
          <button class="btn btn-primary" type="submit"<?= ($canRunPrepaidAmortization && $canCreate) ? '' : ' disabled' ?>>Generate Draft Entries</button>
          <?php if ($editing): ?>
            <a class="btn btn-secondary" href="<?= app_escape($moduleUrl()) ?>">Clear Selection</a>
          <?php endif; ?>
        </div>
      </form>
    </section>
    <?php elseif ($slug === 'journal_entries'): ?>
    <section class="table-card journal-list-card screen-only">
      <div class="table-header">
        <div>
          <h3>Journal Entries</h3>
          <div class="hint"><?= app_escape(app_t(number_format(count($rows)) . ' entry(s), newest accounting dates first')) ?></div>
        </div>
        <?php if ($canCreate): ?>
          <a class="btn btn-secondary" href="<?= app_escape($moduleUrl()) ?>">New Entry</a>
        <?php endif; ?>
      </div>

      <?php if (!$rows): ?>
        <div class="empty-state">No journal entries found for this company.</div>
      <?php else: ?>
        <div class="table-wrap">
          <table class="data-table journal-list-table" style="min-width: 1180px;">
            <thead>
              <tr>
                <th>Entry</th>
                <th>Date</th>
                <th>Reference</th>
                <th>Source</th>
                <th>Lines</th>
                <th>Total Debit</th>
                <th>Total Credit</th>
                <th>Status</th>
                <th class="print-hide">Actions</th>
              </tr>
            </thead>
            <tbody>
              <?php foreach ($rows as $row): ?>
                <?php
                  $rowId = (int) ($row['id'] ?? 0);
                  $rowStatus = strtolower(app_normalize_whitespace((string) ($row['status'] ?? 'draft')));
                  $rowStatusClass = match ($rowStatus) {
                      'posted' => 'badge-green',
                      'reversed' => 'badge-red',
                      default => 'badge-yellow',
                  };
                  $rowSourceType = app_normalize_whitespace((string) ($row['source_type'] ?? ''));
                  $rowSourceId = (int) ($row['source_id'] ?? 0);
                  $rowSourceUrl = app_source_module_url($rowSourceType, $rowSourceId, $companyId);
                  $rowIsSystemGenerated = (int) ($row['is_system_generated'] ?? 0) === 1;
                ?>
                <tr>
                  <td><?= app_escape((string) ($row['entry_number'] ?? ('JE #' . $rowId))) ?></td>
                  <td><?= app_escape(app_date_format((string) ($row['entry_date'] ?? ''))) ?></td>
                  <td><?= app_escape(app_normalize_whitespace((string) ($row['reference'] ?? '')) !== '' ? (string) $row['reference'] : 'N/A') ?></td>
                  <td><?= app_escape($rowSourceType !== '' ? app_source_module_label($rowSourceType) : 'Manual') ?></td>
                  <td><?= app_escape(number_format((int) ($row['line_items_count'] ?? 0))) ?></td>
                  <td><?= app_escape(app_money_format($row['line_items_total_debit'] ?? $row['total_debit'] ?? 0)) ?></td>
                  <td><?= app_escape(app_money_format($row['line_items_total_credit'] ?? $row['total_credit'] ?? 0)) ?></td>
                  <td><span class="badge <?= app_escape($rowStatusClass) ?>"><?= app_escape(ucfirst($rowStatus !== '' ? $rowStatus : 'draft')) ?></span></td>
                  <td class="print-hide">
                    <div class="journal-list-actions">
                      <a class="icon-btn" href="<?= app_escape($moduleUrl(['view' => $rowId])) ?>">View</a>
                      <a class="icon-btn" href="<?= app_escape($moduleUrl(['view' => $rowId, 'print' => '1'])) ?>">Print</a>
                      <?php if (!$rowIsSystemGenerated && $canEdit): ?>
                        <a class="icon-btn" href="<?= app_escape($moduleUrl(['edit' => $rowId])) ?>">Edit</a>
                      <?php else: ?>
                        <button class="icon-btn" type="button" disabled title="Open the source document to edit this entry.">Edit</button>
                      <?php endif; ?>

                      <?php if ($canPost && $rowStatus === 'draft'): ?>
                        <form method="post" class="inline-workflow-form">
                          <?= app_csrf_field() ?>
                          <input type="hidden" name="action" value="workflow">
                          <input type="hidden" name="id" value="<?= $rowId ?>">
                          <input type="hidden" name="target_status" value="posted">
                          <?php if ($companyId !== null): ?>
                            <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
                          <?php endif; ?>
                          <button class="icon-btn primary" type="submit">Post</button>
                        </form>
                      <?php elseif ($canPost && in_array($rowStatus, ['posted', 'reversed'], true)): ?>
                        <form method="post" class="inline-workflow-form">
                          <?= app_csrf_field() ?>
                          <input type="hidden" name="action" value="workflow">
                          <input type="hidden" name="id" value="<?= $rowId ?>">
                          <input type="hidden" name="target_status" value="draft">
                          <?php if ($companyId !== null): ?>
                            <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
                          <?php endif; ?>
                          <button class="icon-btn warning" type="submit">Unpost</button>
                        </form>
                      <?php endif; ?>

                      <?php if ($rowSourceUrl !== null): ?>
                        <a class="icon-btn" href="<?= app_escape($rowSourceUrl) ?>">Source</a>
                      <?php endif; ?>
                      <?php if ($canDelete): ?>
                        <form method="post" class="inline-workflow-form" onsubmit="return confirm('Delete this journal entry?');">
                          <?= app_csrf_field() ?>
                          <input type="hidden" name="action" value="delete">
                          <input type="hidden" name="id" value="<?= $rowId ?>">
                          <?php if ($companyId !== null): ?>
                            <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
                          <?php endif; ?>
                          <button class="icon-btn danger" type="submit">Delete</button>
                        </form>
                      <?php endif; ?>
                    </div>
                  </td>
                </tr>
              <?php endforeach; ?>
            </tbody>
          </table>
        </div>
      <?php endif; ?>
    </section>

    <?php if ($journalIsViewingOnly): ?>
      <section class="panel screen-only">
        <div class="alert info">Viewing the selected journal entry below. Use Edit from the list to change a draft manual entry.</div>
      </section>
    <?php else: ?>
    <section class="panel screen-only journal-editor" data-journal-editor>
      <div class="erp-form-head">
        <div>
          <h3><?= $editing ? app_escape($recordDisplayName) : 'New Manual Journal Entry' ?></h3>
          <span>Enter the journal header, then add balanced journal items below.</span>
        </div>
        <?php if ($editing): ?>
          <?= $statusbarHtml ?>
        <?php endif; ?>
      </div>

      <?php if (!$journalAccountOptions): ?>
        <div class="alert error">No posting accounts are available. Add sub accounts in Chart of Accounts first.</div>
      <?php elseif ($editing && $journalEntryIsSystemGenerated): ?>
        <div class="alert info">This journal entry was generated automatically. Open the source document to change it.</div>
      <?php else: ?>
        <form method="post" class="stacked journal-entry-form">
          <?= app_csrf_field() ?>
          <input type="hidden" name="action" value="save">
          <input type="hidden" name="id" value="<?= (int) ($recordForForm['id'] ?? 0) ?>">
          <input type="hidden" name="status" value="<?= app_escape((string) ($recordForForm['status'] ?? 'draft')) ?>">
          <?php if ($companyId !== null): ?>
            <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
          <?php endif; ?>

          <div class="journal-sheet">
            <div class="journal-sheet-header">
              <div class="journal-title-block">
                <label for="entry_number">Journal Entry</label>
                <input
                  id="entry_number"
                  name="entry_number"
                  type="text"
                  value="<?= app_escape((string) ($recordForForm['entry_number'] ?? '')) ?>"
                  placeholder="Auto-generated"
                  readonly
                >
              </div>
              <div class="journal-header-grid">
                <div class="field">
                  <label for="entry_date">Accounting Date</label>
                  <input id="entry_date" name="entry_date" type="date" value="<?= app_escape((string) ($recordForForm['entry_date'] ?? date('Y-m-d'))) ?>" required>
                </div>
                <div class="field">
                  <label for="reference">Reference</label>
                  <input id="reference" name="reference" type="text" value="<?= app_escape((string) ($recordForForm['reference'] ?? '')) ?>" placeholder="Memo or document reference">
                </div>
                <div class="field journal-field-wide">
                  <label for="description">Description</label>
                  <textarea id="description" name="description" placeholder="Internal note for this entry"><?= app_escape((string) ($recordForForm['description'] ?? '')) ?></textarea>
                </div>
              </div>
            </div>

            <div class="journal-lines-head">
              <div>
                <h4>Journal Items</h4>
                <span>Use debit on one side and credit on the other. Revenue and expense lines require a cost center.</span>
              </div>
              <button class="btn btn-secondary" type="button" data-journal-add-line>Add a line</button>
            </div>

            <div class="table-wrap journal-lines-wrap">
              <table class="data-table journal-lines-table" style="min-width: 1180px;">
                <thead>
                  <tr>
                    <th style="width: 56px;">#</th>
                    <th>Account</th>
                    <th>Label</th>
                    <th>Cost Center</th>
                    <th class="amount-col">Debit</th>
                    <th class="amount-col">Credit</th>
                    <th style="width: 72px;">Remove</th>
                  </tr>
                </thead>
                <tbody data-journal-lines-body>
                  <?php foreach ($journalEditorLines as $lineIndex => $line): ?>
                    <?php
                      $lineAccountCode = app_normalize_whitespace((string) ($line['account_code'] ?? ''));
                      $lineCostCenterCode = app_normalize_whitespace((string) ($line['cost_center_code'] ?? ''));
                      $lineDebit = app_number($line['debit'] ?? 0);
                      $lineCredit = app_number($line['credit'] ?? 0);
                    ?>
                    <tr data-journal-line-row>
                      <td class="journal-line-number" data-journal-line-number><?= (int) $lineIndex + 1 ?></td>
                      <td>
                        <select name="line_account_code[]">
                          <option value="">Choose account</option>
                          <?php foreach ($journalAccountOptions as $optionValue => $optionLabel): ?>
                            <option value="<?= app_escape((string) $optionValue) ?>" <?= $lineAccountCode === (string) $optionValue ? 'selected' : '' ?>>
                              <?= app_escape((string) $optionLabel) ?>
                            </option>
                          <?php endforeach; ?>
                        </select>
                      </td>
                      <td>
                        <input name="line_description[]" type="text" value="<?= app_escape((string) ($line['description'] ?? '')) ?>" placeholder="Label">
                      </td>
                      <td>
                        <select name="line_cost_center_code[]">
                          <option value="">Optional</option>
                          <?php foreach ($journalCostCenterOptions as $optionValue => $optionLabel): ?>
                            <option value="<?= app_escape((string) $optionValue) ?>" <?= $lineCostCenterCode === (string) $optionValue ? 'selected' : '' ?>>
                              <?= app_escape((string) $optionLabel) ?>
                            </option>
                          <?php endforeach; ?>
                        </select>
                      </td>
                      <td>
                        <input class="amount-input" name="line_debit[]" type="number" step="0.01" min="0" value="<?= app_escape($lineDebit > 0 ? (string) $lineDebit : '') ?>" data-journal-debit>
                      </td>
                      <td>
                        <input class="amount-input" name="line_credit[]" type="number" step="0.01" min="0" value="<?= app_escape($lineCredit > 0 ? (string) $lineCredit : '') ?>" data-journal-credit>
                      </td>
                      <td>
                        <button class="icon-btn journal-remove-line" type="button" data-journal-remove-line>Remove</button>
                      </td>
                    </tr>
                  <?php endforeach; ?>
                </tbody>
                <tfoot>
                  <tr>
                    <th colspan="4">Totals</th>
                    <th data-journal-total-debit>0.00</th>
                    <th data-journal-total-credit>0.00</th>
                    <th></th>
                  </tr>
                </tfoot>
              </table>
            </div>

            <div class="journal-total-strip">
              <div>
                <span>Total Debit</span>
                <strong data-journal-total-debit-card>0.00</strong>
              </div>
              <div>
                <span>Total Credit</span>
                <strong data-journal-total-credit-card>0.00</strong>
              </div>
              <div class="journal-difference" data-journal-difference>Difference 0.00</div>
            </div>
          </div>

          <div class="form-actions">
            <?php if (($editing && $canEdit) || (!$editing && $canCreate)): ?>
              <button class="btn btn-primary" type="submit"><?= $editing ? 'Update Entry' : 'Save Entry' ?></button>
            <?php else: ?>
              <span class="hint">You do not have permission to save journal entries.</span>
            <?php endif; ?>
            <?php if ($editing): ?>
              <a class="btn btn-secondary" href="<?= app_escape($moduleUrl()) ?>">Cancel Edit</a>
            <?php endif; ?>
          </div>
        </form>

        <template data-journal-line-template>
          <tr data-journal-line-row>
            <td class="journal-line-number" data-journal-line-number></td>
            <td>
              <select name="line_account_code[]">
                <option value="">Choose account</option>
                <?php foreach ($journalAccountOptions as $optionValue => $optionLabel): ?>
                  <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                <?php endforeach; ?>
              </select>
            </td>
            <td><input name="line_description[]" type="text" placeholder="Label"></td>
            <td>
              <select name="line_cost_center_code[]">
                <option value="">Optional</option>
                <?php foreach ($journalCostCenterOptions as $optionValue => $optionLabel): ?>
                  <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                <?php endforeach; ?>
              </select>
            </td>
            <td><input class="amount-input" name="line_debit[]" type="number" step="0.01" min="0" data-journal-debit></td>
            <td><input class="amount-input" name="line_credit[]" type="number" step="0.01" min="0" data-journal-credit></td>
            <td><button class="icon-btn journal-remove-line" type="button" data-journal-remove-line>Remove</button></td>
          </tr>
        </template>
      <?php endif; ?>
    </section>
    <?php endif; ?>
    <?php else: ?>
    <section class="panel screen-only">
      <div class="erp-form-head">
        <div>
          <h3><?= $editing ? app_escape($recordDisplayName) : app_escape(app_t('New') . ' ' . $title) ?></h3>
          <span><?= app_escape($workflow ? app_t('Use workflow buttons to move this document between states.') : app_t('Manual entry for this module.')) ?></span>
        </div>
        <?php if ($editing): ?>
          <?= $statusbarHtml ?>
        <?php endif; ?>
      </div>
      <form method="post" enctype="multipart/form-data" class="stacked">
        <?= app_csrf_field() ?>
        <input type="hidden" name="action" value="save">
        <input type="hidden" name="id" value="<?= (int) ($recordForForm['id'] ?? 0) ?>">
        <?php if ($companyId !== null): ?>
          <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
        <?php endif; ?>

        <div class="form-grid two">
          <?php foreach ((array) ($definition['fields'] ?? []) as $field): ?>
            <?php
                $fieldName = (string) ($field['name'] ?? '');
                if ($fieldName === '') {
                    continue;
                }
                $fieldValue = $recordForForm[$fieldName] ?? '';
                if ($workflow && $fieldName === (string) ($workflow['field'] ?? 'status')) {
                    echo '<input type="hidden" name="' . app_escape($fieldName) . '" value="' . app_escape((string) $fieldValue) . '">';
                    continue;
                }
                if ($documentLineEditorEnabled && in_array($fieldName, ['is_taxable', 'tax_name', 'tax_rate_percent', 'subtotal', 'discount_total', 'tax_total', 'grand_total'], true)) {
                    echo '<input type="hidden" id="' . app_escape($fieldName) . '" name="' . app_escape($fieldName) . '" value="' . app_escape((string) $fieldValue) . '">';
                    continue;
                }
                echo module_field_input($field, $fieldValue, $definition, $companies, $companyId, $recordForForm);
            ?>
          <?php endforeach; ?>

          <?php if ($slug === 'companies'): ?>
            <div class="field">
              <label for="logo_file">Upload Logo</label>
              <input id="logo_file" name="logo_file" type="file" accept=".png,.jpg,.jpeg,.webp,.svg">
              <div class="field-help">If provided, uploaded logo path will replace logo_path automatically.</div>
            </div>
          <?php endif; ?>

          <?php if ($slug === 'users'): ?>
            <?= module_user_company_access_input($companies, $userCompanyAccessIds) ?>
          <?php endif; ?>
        </div>

        <?php if ($documentLineEditorEnabled): ?>
          <section class="document-lines-editor"
                   data-document-lines-editor
                   data-tax-rates="<?= app_escape(json_encode($invoiceTaxRateMap, JSON_UNESCAPED_UNICODE | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP) ?: '{}') ?>"
                   data-withholding-tax-rates="<?= app_escape(json_encode($invoiceWithholdingTaxRateMap, JSON_UNESCAPED_UNICODE | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP) ?: '{}') ?>"
                   data-vendor-withholding="<?= app_escape(json_encode($vendorWithholdingMap, JSON_UNESCAPED_UNICODE | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG | JSON_HEX_AMP) ?: '{}') ?>">
            <div class="document-lines-head">
              <div>
                <h4>Invoice Lines</h4>
                <span>Each line can be taxable or non-taxable, with its own account and cost center.</span>
              </div>
              <button class="btn btn-secondary" type="button" data-doc-line-add>Add Line</button>
            </div>
            <div class="table-wrap document-lines-wrap">
              <table class="data-table document-lines-table" style="min-width: 1380px;">
                <thead>
                  <tr>
                    <th style="width:48px;">#</th>
                    <th><?= app_escape($documentLineItemLabel) ?></th>
                    <th>Description</th>
                    <th><?= app_escape($documentLineAccountLabel) ?></th>
                    <th>Cost Center</th>
                    <th>Qty</th>
                    <th>Unit Price</th>
                    <th>Discount</th>
                    <th>Taxable</th>
                    <th>Tax Code</th>
                    <th>Tax %</th>
                    <th>Total</th>
                    <th>Remove</th>
                  </tr>
                </thead>
                <tbody data-doc-lines-body>
                  <?php foreach ($documentLineRows as $lineIndex => $line): ?>
                    <?php
                      $lineItem = app_normalize_whitespace((string) ($line['item_code'] ?? ''));
                      $lineAccount = app_normalize_whitespace((string) ($line['account_code'] ?? ''));
                      $lineCostCenter = app_normalize_whitespace((string) ($line['cost_center_code'] ?? ''));
                      $lineTaxName = app_normalize_whitespace((string) ($line['tax_name'] ?? ''));
                      $lineTaxable = !empty($line['is_taxable']) ? 1 : 0;
                    ?>
                    <tr data-doc-line-row>
                      <td data-doc-line-number><?= (int) $lineIndex + 1 ?></td>
                      <td>
                        <?php if ($documentLineItemOptions): ?>
                          <select name="doc_line_item_code[]">
                            <option value="">Choose</option>
                            <?php foreach ($documentLineItemOptions as $optionValue => $optionLabel): ?>
                              <option value="<?= app_escape((string) $optionValue) ?>" <?= $lineItem === (string) $optionValue ? 'selected' : '' ?>><?= app_escape((string) $optionLabel) ?></option>
                            <?php endforeach; ?>
                          </select>
                        <?php else: ?>
                          <input name="doc_line_item_code[]" type="text" value="<?= app_escape($lineItem) ?>" placeholder="Optional">
                        <?php endif; ?>
                      </td>
                      <td><input name="doc_line_description[]" type="text" value="<?= app_escape((string) ($line['description'] ?? '')) ?>" placeholder="Line description"></td>
                      <td>
                        <select name="doc_line_account_code[]">
                          <option value="">Choose</option>
                          <?php foreach ($documentLineAccountOptions as $optionValue => $optionLabel): ?>
                            <option value="<?= app_escape((string) $optionValue) ?>" <?= $lineAccount === (string) $optionValue ? 'selected' : '' ?>><?= app_escape((string) $optionLabel) ?></option>
                          <?php endforeach; ?>
                        </select>
                      </td>
                      <td>
                        <select name="doc_line_cost_center_code[]">
                          <option value="">Choose</option>
                          <?php foreach ($documentLineCostCenterOptions as $optionValue => $optionLabel): ?>
                            <option value="<?= app_escape((string) $optionValue) ?>" <?= $lineCostCenter === (string) $optionValue ? 'selected' : '' ?>><?= app_escape((string) $optionLabel) ?></option>
                          <?php endforeach; ?>
                        </select>
                      </td>
                      <td><input class="amount-input" name="doc_line_quantity[]" type="number" step="0.0001" min="0" value="<?= app_escape((string) app_number($line['quantity'] ?? 1)) ?>" data-doc-line-qty></td>
                      <td><input class="amount-input" name="doc_line_unit_price[]" type="number" step="0.01" min="0" value="<?= app_escape((string) app_number($line['unit_price'] ?? 0)) ?>" data-doc-line-unit></td>
                      <td><input class="amount-input" name="doc_line_discount_total[]" type="number" step="0.01" min="0" value="<?= app_escape((string) app_number($line['discount_total'] ?? 0)) ?>" data-doc-line-discount></td>
                      <td class="centered-cell">
                        <input type="hidden" name="doc_line_is_taxable[]" value="<?= $lineTaxable ?>" data-doc-line-taxable-value>
                        <input type="checkbox" value="1" <?= $lineTaxable ? 'checked' : '' ?> data-doc-line-taxable>
                      </td>
                      <td>
                        <select name="doc_line_tax_name[]" data-doc-line-tax-name>
                          <option value="">Non-taxable</option>
                          <?php foreach ($documentLineTaxOptions as $optionValue => $optionLabel): ?>
                            <option value="<?= app_escape((string) $optionValue) ?>" <?= $lineTaxName === (string) $optionValue ? 'selected' : '' ?>><?= app_escape((string) $optionLabel) ?></option>
                          <?php endforeach; ?>
                        </select>
                      </td>
                      <td><input class="amount-input" name="doc_line_tax_rate_percent[]" type="number" step="0.01" min="0" value="<?= app_escape((string) app_number($line['tax_rate_percent'] ?? 0)) ?>" data-doc-line-tax-rate></td>
                      <td>
                        <strong data-doc-line-total><?= app_escape(app_money_format($line['line_total'] ?? 0)) ?></strong>
                        <input type="hidden" name="doc_line_subtotal[]" value="<?= app_escape((string) app_number($line['subtotal'] ?? 0)) ?>" data-doc-line-subtotal-value>
                        <input type="hidden" name="doc_line_tax_total[]" value="<?= app_escape((string) app_number($line['tax_total'] ?? 0)) ?>" data-doc-line-tax-value>
                        <input type="hidden" name="doc_line_total[]" value="<?= app_escape((string) app_number($line['line_total'] ?? 0)) ?>" data-doc-line-total-value>
                      </td>
                      <td><button class="icon-btn danger" type="button" data-doc-line-remove>Remove</button></td>
                    </tr>
                  <?php endforeach; ?>
                </tbody>
              </table>
            </div>
            <div class="document-line-summary">
              <div><span>Subtotal</span><strong data-doc-lines-subtotal>0.00</strong></div>
              <div><span>Discount</span><strong data-doc-lines-discount>0.00</strong></div>
              <div><span>VAT</span><strong data-doc-lines-tax>0.00</strong></div>
              <?php if ($slug === 'purchase_bills'): ?>
                <div><span>Commercial Profit Tax</span><strong data-doc-lines-withholding>0.00</strong></div>
                <div><span>Net Payable</span><strong data-doc-lines-net-payable>0.00</strong></div>
              <?php endif; ?>
              <div class="is-total"><span>Grand Total</span><strong data-doc-lines-grand>0.00</strong></div>
            </div>

            <template data-doc-line-template>
              <tr data-doc-line-row>
                <td data-doc-line-number></td>
                <td>
                  <?php if ($documentLineItemOptions): ?>
                    <select name="doc_line_item_code[]">
                      <option value="">Choose</option>
                      <?php foreach ($documentLineItemOptions as $optionValue => $optionLabel): ?>
                        <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                      <?php endforeach; ?>
                    </select>
                  <?php else: ?>
                    <input name="doc_line_item_code[]" type="text" placeholder="Optional">
                  <?php endif; ?>
                </td>
                <td><input name="doc_line_description[]" type="text" placeholder="Line description"></td>
                <td>
                  <select name="doc_line_account_code[]">
                    <option value="">Choose</option>
                    <?php foreach ($documentLineAccountOptions as $optionValue => $optionLabel): ?>
                      <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                    <?php endforeach; ?>
                  </select>
                </td>
                <td>
                  <select name="doc_line_cost_center_code[]">
                    <option value="">Choose</option>
                    <?php foreach ($documentLineCostCenterOptions as $optionValue => $optionLabel): ?>
                      <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                    <?php endforeach; ?>
                  </select>
                </td>
                <td><input class="amount-input" name="doc_line_quantity[]" type="number" step="0.0001" min="0" value="1" data-doc-line-qty></td>
                <td><input class="amount-input" name="doc_line_unit_price[]" type="number" step="0.01" min="0" data-doc-line-unit></td>
                <td><input class="amount-input" name="doc_line_discount_total[]" type="number" step="0.01" min="0" value="0" data-doc-line-discount></td>
                <td class="centered-cell"><input type="hidden" name="doc_line_is_taxable[]" value="0" data-doc-line-taxable-value><input type="checkbox" value="1" data-doc-line-taxable></td>
                <td>
                  <select name="doc_line_tax_name[]" data-doc-line-tax-name>
                    <option value="">Non-taxable</option>
                    <?php foreach ($documentLineTaxOptions as $optionValue => $optionLabel): ?>
                      <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                    <?php endforeach; ?>
                  </select>
                </td>
                <td><input class="amount-input" name="doc_line_tax_rate_percent[]" type="number" step="0.01" min="0" value="0" data-doc-line-tax-rate></td>
                <td>
                  <strong data-doc-line-total>0.00</strong>
                  <input type="hidden" name="doc_line_subtotal[]" value="0" data-doc-line-subtotal-value>
                  <input type="hidden" name="doc_line_tax_total[]" value="0" data-doc-line-tax-value>
                  <input type="hidden" name="doc_line_total[]" value="0" data-doc-line-total-value>
                </td>
                <td><button class="icon-btn danger" type="button" data-doc-line-remove>Remove</button></td>
              </tr>
            </template>
          </section>
        <?php endif; ?>

        <?php if (in_array($slug, ['sales_invoices', 'student_invoices', 'purchase_bills', 'expenses', 'prepaid_expenses', 'sales_returns', 'purchase_returns', 'expense_returns'], true)): ?>
          <div class="tax-live-summary" data-tax-live-summary>
            <div>
              <span>Subtotal after discount</span>
              <strong data-tax-summary-base>0.00</strong>
            </div>
            <div>
              <span>Tax</span>
              <strong data-tax-summary-tax>0.00</strong>
            </div>
            <?php if ($slug === 'purchase_bills'): ?>
              <div>
                <span>Commercial Profit Tax</span>
                <strong data-tax-summary-withholding>0.00</strong>
              </div>
              <div>
                <span>Net Payable</span>
                <strong data-tax-summary-net-payable>0.00</strong>
              </div>
            <?php endif; ?>
            <div class="is-total">
              <span>Grand Total</span>
              <strong data-tax-summary-grand>0.00</strong>
            </div>
          </div>
        <?php endif; ?>

        <div class="form-actions">
          <?php if (($editing && $canEdit) || (!$editing && $canCreate)): ?>
            <button class="btn btn-primary" type="submit"><?= $editing ? 'Update' : 'Save' ?></button>
          <?php else: ?>
            <span class="hint">You do not have permission to save records in this module.</span>
          <?php endif; ?>
          <?php if ($editing): ?>
            <a class="btn btn-secondary" href="<?= app_escape($moduleUrl()) ?>">Cancel Edit</a>
          <?php endif; ?>
        </div>
      </form>
    </section>

    <?php if ($editing && $slug === 'ifrs16_leases'): ?>
      <section class="panel screen-only">
        <div class="section-heading">
          <h3>Generate IFRS16 Draft Entries</h3>
          <span>Initial recognition plus recurring interest and ROU depreciation journals. Bank payment is posted manually from Payments.</span>
          <form method="post" enctype="multipart/form-data" class="inline-workflow-form">
            <?= app_csrf_field() ?>
            <input type="hidden" name="action" value="upload_ifrs16_payments">
            <input type="hidden" name="id" value="<?= (int) ($recordForForm['id'] ?? 0) ?>">
            <?php if ($companyId !== null): ?>
              <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
            <?php endif; ?>
            <input name="ifrs16_payment_file" type="file" accept=".xlsx,.csv,.txt"<?= $canEdit ? '' : ' disabled' ?>>
            <button class="btn btn-secondary" type="submit"<?= $canEdit ? '' : ' disabled' ?>>Upload Payments</button>
          </form>
          <form method="post" class="inline-workflow-form">
            <?= app_csrf_field() ?>
            <input type="hidden" name="action" value="generate_ifrs16_schedule">
            <input type="hidden" name="id" value="<?= (int) ($recordForForm['id'] ?? 0) ?>">
            <?php if ($companyId !== null): ?>
              <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
            <?php endif; ?>
            <button class="btn btn-primary" type="submit"<?= $canCreate ? '' : ' disabled' ?>>Generate Draft Entries</button>
          </form>
        </div>

        <div class="workspace-kpis" style="margin-bottom:18px;">
          <div class="workspace-kpi">
            <span>PV Lease Liability</span>
            <strong><?= app_escape(app_money_format($recordForForm['pv_lease_liability'] ?? 0)) ?></strong>
          </div>
          <div class="workspace-kpi">
            <span>Initial ROU Asset</span>
            <strong><?= app_escape(app_money_format($recordForForm['initial_rou_asset'] ?? 0)) ?></strong>
          </div>
          <div class="workspace-kpi">
            <span>Total Interest</span>
            <strong><?= app_escape(app_money_format($ifrs16ScheduleTotals['interest_expense'] ?? $recordForForm['total_interest'] ?? 0)) ?></strong>
          </div>
          <div class="workspace-kpi">
            <span>Payment Input PV</span>
            <strong><?= app_escape(app_money_format($ifrs16PaymentTotals['pv_at_commencement'] ?? 0)) ?></strong>
          </div>
          <div class="workspace-kpi">
            <span>Schedule Lines</span>
            <strong><?= app_escape(number_format(count($ifrs16ScheduleLines))) ?></strong>
          </div>
        </div>

        <?php if ($ifrs16PaymentRows): ?>
          <div class="table-wrap" style="margin-bottom:18px;">
            <table class="data-table" style="min-width: 980px;">
              <thead>
                <tr>
                  <th>#</th>
                  <th>Payment Date</th>
                  <th>Payment</th>
                  <th>Type</th>
                  <th>Included</th>
                  <th>Days</th>
                  <th>Discount Factor</th>
                  <th>PV at Commencement</th>
                  <th>Notes</th>
                </tr>
              </thead>
              <tbody>
                <?php foreach ($ifrs16PaymentRows as $paymentRow): ?>
                  <tr>
                    <td><?= app_escape((string) ($paymentRow['sequence_no'] ?? '')) ?></td>
                    <td><?= app_escape(app_date_format((string) ($paymentRow['payment_date'] ?? ''))) ?></td>
                    <td><?= app_escape(app_money_format($paymentRow['payment_amount'] ?? 0)) ?></td>
                    <td><?= app_escape((string) ($paymentRow['payment_type'] ?? '')) ?></td>
                    <td><?= !empty($paymentRow['included_in_liability']) ? 'Yes' : 'No' ?></td>
                    <td><?= app_escape((string) ($paymentRow['days_from_commencement'] ?? 0)) ?></td>
                    <td><?= app_escape(number_format((float) ($paymentRow['discount_factor'] ?? 0), 8, '.', '')) ?></td>
                    <td><?= app_escape(app_money_format($paymentRow['pv_at_commencement'] ?? 0)) ?></td>
                    <td><?= app_escape((string) ($paymentRow['notes'] ?? '')) ?></td>
                  </tr>
                <?php endforeach; ?>
              </tbody>
              <tfoot>
                <tr>
                  <th colspan="2">Totals</th>
                  <th><?= app_escape(app_money_format($ifrs16PaymentTotals['payment_amount'] ?? 0)) ?></th>
                  <th colspan="4"></th>
                  <th><?= app_escape(app_money_format($ifrs16PaymentTotals['pv_at_commencement'] ?? 0)) ?></th>
                  <th></th>
                </tr>
              </tfoot>
            </table>
          </div>
        <?php endif; ?>

        <?php if (!$ifrs16ScheduleLines): ?>
          <div class="empty-state">No schedule lines generated yet.</div>
        <?php else: ?>
          <div class="table-wrap">
            <table class="data-table" style="min-width: 1180px;">
              <thead>
                <tr>
                  <th>#</th>
                  <th>Date</th>
                  <th>Opening</th>
                  <th>Interest</th>
                  <th>Payment</th>
                  <th>Liability Reduction</th>
                  <th>Closing</th>
                  <th>Depreciation</th>
                  <th>ROU NBV</th>
                  <th>Status</th>
                  <th>Journal</th>
                </tr>
              </thead>
              <tbody>
                <?php foreach ($ifrs16ScheduleLines as $scheduleLine): ?>
                  <?php
                    $scheduleStatus = strtolower(app_normalize_whitespace((string) ($scheduleLine['status'] ?? 'draft')));
                    $journalEntryId = (int) ($scheduleLine['journal_entry_id'] ?? 0);
                    $journalEntryNumber = app_normalize_whitespace((string) ($scheduleLine['journal_entry_number'] ?? ''));
                  ?>
                  <tr>
                    <td><?= app_escape((string) ($scheduleLine['sequence_no'] ?? '')) ?></td>
                    <td><?= app_escape(app_date_format((string) ($scheduleLine['schedule_date'] ?? ''))) ?></td>
                    <td><?= app_escape(app_money_format($scheduleLine['opening_liability'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($scheduleLine['interest_expense'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($scheduleLine['payment_amount'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($scheduleLine['liability_reduction'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($scheduleLine['closing_liability'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($scheduleLine['depreciation_expense'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($scheduleLine['rou_asset_nbv'] ?? 0)) ?></td>
                    <td><span class="badge <?= $scheduleStatus === 'posted' ? 'badge-green' : ($scheduleStatus === 'cancelled' ? 'badge-red' : 'badge-yellow') ?>"><?= app_escape(ucfirst($scheduleStatus !== '' ? $scheduleStatus : 'draft')) ?></span></td>
                    <td>
                      <?php if ($journalEntryId > 0): ?>
                        <a class="icon-btn" href="module.php?module=journal_entries&view=<?= $journalEntryId ?>&company_id=<?= (int) $companyId ?>"><?= app_escape($journalEntryNumber !== '' ? $journalEntryNumber : 'Open') ?></a>
                      <?php else: ?>
                        <span class="hint">Draft not linked</span>
                      <?php endif; ?>
                    </td>
                  </tr>
                <?php endforeach; ?>
              </tbody>
              <tfoot>
                <tr>
                  <th colspan="3">Totals</th>
                  <th><?= app_escape(app_money_format($ifrs16ScheduleTotals['interest_expense'] ?? 0)) ?></th>
                  <th><?= app_escape(app_money_format($ifrs16ScheduleTotals['payment_amount'] ?? 0)) ?></th>
                  <th></th>
                  <th></th>
                  <th><?= app_escape(app_money_format($ifrs16ScheduleTotals['depreciation_expense'] ?? 0)) ?></th>
                  <th colspan="3"></th>
                </tr>
              </tfoot>
            </table>
          </div>
        <?php endif; ?>
      </section>
    <?php endif; ?>

    <?php if ($editing && $documentLineEditorEnabled && in_array($slug, ['sales_invoices', 'student_invoices', 'purchase_bills', 'sales_returns', 'purchase_returns', 'expense_returns'], true)): ?>
      <?php
        $isStudentInvoicePrint = $slug === 'student_invoices';
        $isSalesInvoicePrint = $slug === 'sales_invoices';
        $isPurchaseBillPrint = $slug === 'purchase_bills';
        $isReturnPrint = in_array($slug, ['sales_returns', 'purchase_returns', 'expense_returns'], true);
        $isPurchaseSidePrint = in_array($slug, ['purchase_bills', 'purchase_returns', 'expense_returns'], true);
        $invoiceNumberField = match ($slug) {
            'student_invoices' => 'student_invoice_number',
            'purchase_bills' => 'bill_number',
            'sales_returns', 'purchase_returns', 'expense_returns' => 'return_number',
            default => 'invoice_number',
        };
        $invoiceDateField = match ($slug) {
            'purchase_bills' => 'bill_date',
            'sales_returns', 'purchase_returns', 'expense_returns' => 'return_date',
            default => 'invoice_date',
        };
        $invoiceNumber = app_normalize_whitespace((string) ($recordForForm[$invoiceNumberField] ?? ''));
        $invoiceDate = app_sanitize_date((string) ($recordForForm[$invoiceDateField] ?? date('Y-m-d')), date('Y-m-d'));
        $printTitle = match ($slug) {
            'student_invoices' => 'Student Tax Invoice',
            'purchase_bills' => 'Vendor Bill',
            'sales_returns' => 'Tax Credit Note / Sales Return',
            'purchase_returns' => 'Purchase Return',
            'expense_returns' => 'Expense Return',
            default => 'Tax Invoice',
        };
        $contactCode = match ($slug) {
            'student_invoices' => app_normalize_whitespace((string) ($recordForForm['guardian_contact_code'] ?? '')),
            'purchase_bills', 'purchase_returns' => app_normalize_whitespace((string) ($recordForForm['vendor_code'] ?? '')),
            'expense_returns' => '',
            default => app_normalize_whitespace((string) ($recordForForm['customer_code'] ?? '')),
        };
        $contactLookup = $isPurchaseSidePrint ? 'vendor_contact_code' : 'customer_contact_code';
        $contactOptions = $companyId !== null && $companyId > 0 ? app_module_lookup_options($contactLookup, (int) $companyId) : [];
        $studentOptions = $companyId !== null && $companyId > 0 ? app_module_lookup_options('student_number', (int) $companyId) : [];
        $studentCode = app_normalize_whitespace((string) ($recordForForm['student_number'] ?? ''));
        $studentLabel = $studentCode !== '' ? (string) ($studentOptions[$studentCode] ?? $studentCode) : '';
        $printLines = $companyId !== null && $companyId > 0 ? app_fetch_document_lines($slug, (int) $companyId, (int) ($recordForForm['id'] ?? 0), $recordForForm) : app_document_line_from_record($slug, $recordForForm);
        $qrValue = ($isSalesInvoicePrint || $isStudentInvoicePrint || $slug === 'sales_returns') ? app_zatca_tlv_base64($company, $recordForForm, $invoiceDateField) : '';
        $invoiceStatus = app_normalize_whitespace((string) ($recordForForm['status'] ?? ''));
        $documentAmount = app_number($recordForForm['net_payable'] ?? $recordForForm['grand_total'] ?? 0);
        $amountInWords = app_amount_in_words_ar($documentAmount);
        $barcodeSeed = $invoiceNumber !== '' ? $invoiceNumber : ('DOC-' . (int) ($recordForForm['id'] ?? 0));
        $barcodeHash = md5($barcodeSeed);
        $barcodeBars = [];
        for ($barIndex = 0; $barIndex < 34; $barIndex++) {
            $hex = hexdec($barcodeHash[$barIndex % strlen($barcodeHash)]);
            $barcodeBars[] = [
                'w' => 1 + ($hex % 4),
                'h' => 34 + (($hex * 7) % 26),
            ];
        }
      ?>
      <section id="invoicePrintArea" class="tax-invoice-print-card print-scope" data-print-scope data-default-print-target>
        <div class="invoice-print-head">
          <div class="invoice-brand">
            <img src="<?= app_escape(app_logo_path_for_header($company)) ?>" alt="Company Logo">
            <div>
              <strong><?= app_escape((string) ($company['name'] ?? app_config('app_name'))) ?></strong>
              <span><?= app_escape((string) ($company['legal_name'] ?? '')) ?></span>
              <small>VAT: <?= app_escape(app_normalize_whitespace((string) ($company['vat_number'] ?? 'Not set'))) ?></small>
            </div>
          </div>
          <div class="tax-invoice-heading">
            <span><?= app_escape($isReturnPrint ? 'RETURN DOCUMENT' : 'TAX INVOICE') ?></span>
            <h1><?= app_escape($printTitle) ?></h1>
          </div>
          <div class="invoice-print-actions screen-only">
            <button class="btn btn-secondary" type="button" data-print-page data-print-target="#invoicePrintArea">Print</button>
            <a class="btn btn-secondary" href="<?= app_escape($moduleExportUrl) ?>">Export XLSX</a>
          </div>
        </div>

        <div class="invoice-title-row">
          <div>
            <span><?= app_escape($isReturnPrint ? 'Return No.' : 'Invoice No.') ?></span>
            <strong><?= app_escape($invoiceNumber !== '' ? $invoiceNumber : ('Document #' . (int) ($recordForForm['id'] ?? 0))) ?></strong>
          </div>
          <div class="invoice-status-chip"><?= app_escape($invoiceStatus !== '' ? ucfirst(str_replace('_', ' ', $invoiceStatus)) : 'Issued') ?></div>
        </div>

        <div class="invoice-meta-grid">
          <div><span>Date</span><strong><?= app_escape(app_date_format($invoiceDate)) ?></strong></div>
          <div><span><?= $isPurchaseSidePrint ? ($slug === 'expense_returns' ? 'Source' : 'Vendor') : 'Customer' ?></span><strong><?= app_escape((string) ($contactOptions[$contactCode] ?? ($contactCode !== '' ? $contactCode : 'N/A'))) ?></strong></div>
          <?php if ($isReturnPrint): ?>
            <div><span>Return Type</span><strong><?= app_escape(ucfirst((string) ($recordForForm['return_type'] ?? 'Partial'))) ?></strong></div>
          <?php endif; ?>
          <?php if ($isStudentInvoicePrint): ?>
            <div><span>Student</span><strong><?= app_escape($studentLabel) ?></strong></div>
            <div><span>Stage / Grade</span><strong><?= app_escape((string) ($recordForForm['stage_code'] ?? '')) ?> / <?= app_escape((string) ($recordForForm['grade_code'] ?? '')) ?></strong></div>
          <?php endif; ?>
          <div><span>Reference</span><strong><?= app_escape((string) ($recordForForm['description'] ?? $recordForForm['notes'] ?? 'N/A')) ?></strong></div>
          <div><span>Cost Center</span><strong><?= app_escape((string) ($recordForForm['cost_center_code'] ?? 'N/A')) ?></strong></div>
        </div>

        <div class="invoice-print-body">
          <div class="table-wrap">
            <table class="data-table invoice-lines-table">
              <thead>
                <tr>
                  <th>#</th>
                  <th>Item</th>
                  <th>Description</th>
                  <th>Qty</th>
                  <th>Unit Price</th>
                  <th>Discount</th>
                  <th>Taxable Amount</th>
                  <th>VAT</th>
                  <th>Total</th>
                </tr>
              </thead>
              <tbody>
                <?php foreach ($printLines as $lineIndex => $line): ?>
                  <tr>
                    <td><?= (int) $lineIndex + 1 ?></td>
                    <td><?= app_escape((string) ($line['item_code'] ?? '')) ?></td>
                    <td><?= app_escape((string) ($line['description'] ?? '')) ?></td>
                    <td><?= app_escape(app_money_format($line['quantity'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($line['unit_price'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($line['discount_total'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($line['subtotal'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($line['tax_total'] ?? 0)) ?></td>
                    <td><strong><?= app_escape(app_money_format($line['line_total'] ?? 0)) ?></strong></td>
                  </tr>
                <?php endforeach; ?>
              </tbody>
            </table>
          </div>

          <div class="invoice-amount-words" dir="rtl">
            <span>الإجمالي</span>
            <strong><?= app_escape($amountInWords) ?></strong>
          </div>

          <div class="invoice-summary-panel">
            <div class="invoice-qr-box">
              <?php if ($qrValue !== ''): ?>
                <div class="zatca-qr" data-zatca-qr-code data-zatca-value="<?= app_escape($qrValue) ?>"></div>
                <small>ZATCA QR / Barcode</small>
              <?php else: ?>
                <div class="invoice-barcode invoice-barcode--summary" aria-label="Document barcode">
                  <?php foreach ($barcodeBars as $bar): ?>
                    <span style="width: <?= (int) $bar['w'] ?>px; height: <?= (int) $bar['h'] ?>px;"></span>
                  <?php endforeach; ?>
                </div>
                <small><?= app_escape($barcodeSeed) ?></small>
              <?php endif; ?>
            </div>
            <div class="invoice-totals-table">
              <dl>
                <div><dt>Subtotal</dt><dd><img class="sar-mark" src="assets/saudi-riyal.png" alt="SAR"> <?= app_escape(app_money_format($recordForForm['subtotal'] ?? 0)) ?></dd></div>
                <div><dt>Discount</dt><dd><img class="sar-mark" src="assets/saudi-riyal.png" alt="SAR"> <?= app_escape(app_money_format($recordForForm['discount_total'] ?? 0)) ?></dd></div>
                <div><dt>VAT</dt><dd><img class="sar-mark" src="assets/saudi-riyal.png" alt="SAR"> <?= app_escape(app_money_format($recordForForm['tax_total'] ?? 0)) ?></dd></div>
                <div class="is-total"><dt>Grand Total</dt><dd><img class="sar-mark sar-mark--light" src="assets/saudi-riyal.png" alt="SAR"> <?= app_escape(app_money_format($recordForForm['grand_total'] ?? 0)) ?></dd></div>
                <?php if ($isPurchaseBillPrint): ?>
                  <div><dt>Commercial Profit Tax</dt><dd><img class="sar-mark" src="assets/saudi-riyal.png" alt="SAR"> <?= app_escape(app_money_format($recordForForm['withholding_tax_total'] ?? 0)) ?></dd></div>
                  <div class="is-total"><dt>Net Payable</dt><dd><img class="sar-mark sar-mark--light" src="assets/saudi-riyal.png" alt="SAR"> <?= app_escape(app_money_format($recordForForm['net_payable'] ?? $recordForForm['grand_total'] ?? 0)) ?></dd></div>
                <?php endif; ?>
                <div><dt><?= $isReturnPrint ? 'Refunded' : 'Paid' ?></dt><dd><img class="sar-mark" src="assets/saudi-riyal.png" alt="SAR"> <?= app_escape(app_money_format($recordForForm['paid_amount'] ?? $recordForForm['inline_paid_amount'] ?? 0)) ?></dd></div>
              </dl>
            </div>
          </div>
        </div>
      </section>
    <?php endif; ?>

    <?php if ($canImport && !in_array($slug, ['depreciation_runs', 'prepaid_amortization_runs', 'ifrs16_lease_schedules'], true)): ?>
    <section class="panel screen-only">
      <div class="section-heading">
        <h3>Import CSV / XLSX</h3>
        <span>Headers should match field names or labels</span>
        <a class="btn btn-secondary" href="<?= app_escape($moduleSampleUrl) ?>">Download Sample XLSX</a>
      </div>
      <form method="post" enctype="multipart/form-data" class="form-grid two">
        <?= app_csrf_field() ?>
        <input type="hidden" name="action" value="import">
        <?php if ($companyId !== null): ?>
          <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
        <?php endif; ?>
        <div class="field">
          <label for="import_file">File</label>
          <input id="import_file" name="import_file" type="file" accept=".csv,.txt,.xlsx" required>
          <div class="field-help">Supported: CSV and XLSX. First non-empty row is used as header.</div>
        </div>
        <div class="field">
          <label>Expected Fields</label>
          <input type="text" value="<?= app_escape(implode(', ', array_map(static fn (array $f): string => (string) ($f['name'] ?? ''), (array) ($definition['fields'] ?? [])))) ?>" readonly>
        </div>
        <div class="form-actions">
          <button class="btn btn-primary" type="submit">Start Import</button>
        </div>
      </form>
    </section>
    <?php endif; ?>
    <?php endif; ?>

    <?php if ($slug !== 'journal_entries' && $slug === 'chart_of_accounts'): ?>
    <section class="table-card coa-tree-card" data-coa-tree<?= $bulkSelectionEnabled ? ' data-bulk-scope' : '' ?>>
      <div class="table-header">
        <div>
          <h3><?= app_escape(app_t('Chart of Accounts Tree')) ?></h3>
          <div class="hint"><?= app_escape(app_t(number_format(count($rows)) . ' row(s)')) ?></div>
        </div>
        <div class="bulk-actions screen-only">
          <?php if ($rows): ?>
            <button class="btn btn-secondary" type="button" data-coa-expand-all><?= app_escape(app_t('Expand All')) ?></button>
            <button class="btn btn-ghost" type="button" data-coa-collapse-all><?= app_escape(app_t('Collapse All')) ?></button>
          <?php endif; ?>
          <?php if ($bulkSelectionEnabled && $rows): ?>
            <button class="btn btn-secondary" type="button" data-bulk-select-mode><?= app_escape(app_t('Select')) ?></button>
            <button class="btn btn-secondary" type="button" data-bulk-select-all><?= app_escape(app_t('Select All')) ?></button>
            <button class="btn btn-ghost" type="button" data-bulk-clear><?= app_escape(app_t('Clear Selection')) ?></button>
            <?php if ($bulkStatementGroupEnabled): ?>
              <select name="financial_statement_group_code" form="moduleBulkDeleteForm" data-bulk-statement-group-select>
                <option value=""><?= app_escape(app_t('Choose Financial Statement Group')) ?></option>
                <?php foreach ($bulkStatementGroupOptions as $optionValue => $optionLabel): ?>
                  <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                <?php endforeach; ?>
              </select>
              <button class="btn btn-secondary" type="submit" form="moduleBulkDeleteForm" name="action" value="bulk_assign_statement_group" data-bulk-assign disabled><?= app_escape(app_t('Assign Group')) ?></button>
            <?php endif; ?>
            <?php if ($bulkDeleteEnabled): ?>
              <button class="btn btn-danger" type="submit" form="moduleBulkDeleteForm" name="action" value="bulk_delete" data-bulk-delete disabled><?= app_escape(app_t('Delete Selected')) ?></button>
            <?php endif; ?>
            <span class="bulk-count" data-bulk-count>0 <?= app_escape(app_t('selected')) ?></span>
          <?php endif; ?>
        </div>
      </div>

      <?php if (!$rows): ?>
        <div class="empty-state">No accounts found for this company context.</div>
      <?php else: ?>
        <?php if ($bulkSelectionEnabled): ?>
          <form id="moduleBulkDeleteForm" method="post" class="bulk-delete-form" data-bulk-delete-form data-confirm-message="<?= app_escape(app_t('Delete selected records?')) ?>" data-assign-confirm-message="<?= app_escape(app_t('Assign selected accounts to this financial statement group?')) ?>">
            <?= app_csrf_field() ?>
            <?php if ($companyId !== null): ?>
              <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
            <?php endif; ?>
          </form>
        <?php endif; ?>
        <div class="coa-tree-head" aria-hidden="true">
          <span><?= app_escape(app_t('Account')) ?></span>
          <span><?= app_escape(app_t('Type / Group / Balance / Active')) ?></span>
          <span><?= app_escape(app_t('Actions')) ?></span>
        </div>
        <div class="coa-tree-list">
          <?php foreach ((array) ($chartAccountTreeData['roots'] ?? []) as $rootAccount): ?>
            <?= module_chart_account_node_html(
                $rootAccount,
                0,
                (array) ($chartAccountTreeData['children_by_parent'] ?? []),
                (array) ($chartAccountTreeData['rows_by_code'] ?? []),
                (array) ($chartAccountCodeData['children_by_parent'] ?? []),
                (array) ($chartAccountCodeData['rows_by_code'] ?? []),
                $moduleUrl,
                $canCreate,
                $canEdit,
                $canDelete,
                $bulkSelectionEnabled,
                $companyId,
                $definition,
                $companies
            ) ?>
          <?php endforeach; ?>
        </div>
      <?php endif; ?>

      <?php if ($canCreate): ?>
        <div class="coa-modal" data-coa-modal hidden role="dialog" aria-modal="true" aria-labelledby="coaChildModalTitle">
          <div class="coa-modal__panel">
            <div class="coa-modal__head">
              <div>
                <h3 id="coaChildModalTitle"><?= app_escape(app_t('Add Sub Account')) ?></h3>
                <span><?= app_escape(app_t('The child account code is generated from the selected parent account.')) ?></span>
              </div>
              <button class="icon-btn" type="button" data-coa-modal-close aria-label="<?= app_escape(app_t('Close')) ?>">x</button>
            </div>
            <form method="post" class="form-grid two">
              <?= app_csrf_field() ?>
              <input type="hidden" name="action" value="save">
              <?php if ($companyId !== null): ?>
                <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
              <?php endif; ?>
              <input type="hidden" name="parent_code" data-coa-modal-parent-code>

              <div class="field">
                <label for="coa_child_parent_display"><?= app_escape(app_t('Parent Account')) ?></label>
                <input id="coa_child_parent_display" type="text" data-coa-modal-parent-label readonly>
              </div>
              <div class="field">
                <label for="coa_child_account_code"><?= app_escape(app_t('Account Code')) ?></label>
                <input id="coa_child_account_code" name="account_code" type="text" data-coa-modal-account-code required>
                <div class="field-help"><?= app_escape(app_t('Auto-generated; you can adjust it before saving if needed.')) ?></div>
              </div>
              <div class="field">
                <label for="coa_child_account_name"><?= app_escape(app_t('Account Name')) ?></label>
                <input id="coa_child_account_name" name="account_name" type="text" data-coa-modal-account-name required>
              </div>
              <div class="field">
                <label for="coa_child_account_type"><?= app_escape(app_t('Type')) ?></label>
                <select id="coa_child_account_type" name="account_type" data-coa-modal-account-type required>
                  <option value=""><?= app_escape(app_t('Choose')) ?></option>
                  <?php foreach ($chartAccountTypeOptions as $optionValue => $optionLabel): ?>
                    <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                  <?php endforeach; ?>
                </select>
              </div>
              <div class="field">
                <label for="coa_child_opening_balance"><?= app_escape(app_t('Opening Balance')) ?></label>
                <input id="coa_child_opening_balance" name="opening_balance" type="number" step="0.01" value="0">
              </div>
              <div class="field">
                <label for="coa_child_statement_group"><?= app_escape(app_t('Financial Statement Group')) ?></label>
                <select id="coa_child_statement_group" name="financial_statement_group_code" data-coa-modal-statement-group>
                  <option value=""><?= app_escape(app_t('Choose')) ?></option>
                  <?php foreach ($chartStatementGroupOptions as $optionValue => $optionLabel): ?>
                    <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                  <?php endforeach; ?>
                </select>
              </div>
              <div class="field checkbox-field">
                <label for="coa_child_is_active" style="display:flex;align-items:center;gap:8px;">
                  <input id="coa_child_is_active" name="is_active" type="checkbox" value="1" checked>
                  <?= app_escape(app_t('Active')) ?>
                </label>
              </div>
              <div class="form-actions">
                <button class="btn btn-ghost" type="button" data-coa-modal-close><?= app_escape(app_t('Cancel')) ?></button>
                <button class="btn btn-primary" type="submit"><?= app_escape(app_t('Save')) ?></button>
              </div>
            </form>
          </div>
        </div>
      <?php endif; ?>
    </section>
    <?php endif; ?>

    <?php if ($slug !== 'journal_entries' && $slug !== 'chart_of_accounts'): ?>
    <section class="table-card"<?= $bulkSelectionEnabled ? ' data-bulk-scope' : '' ?>>
      <div class="table-header">
        <div>
          <h3>Records</h3>
          <div class="hint"><?= app_escape(app_t(number_format(count($rows)) . ' row(s)')) ?></div>
        </div>
        <?php if ($bulkSelectionEnabled && $rows): ?>
          <div class="bulk-actions screen-only">
            <button class="btn btn-secondary" type="button" data-bulk-select-mode><?= app_escape(app_t('Select')) ?></button>
            <button class="btn btn-secondary" type="button" data-bulk-select-all><?= app_escape(app_t('Select All')) ?></button>
            <button class="btn btn-ghost" type="button" data-bulk-clear><?= app_escape(app_t('Clear Selection')) ?></button>
            <?php if ($bulkStatementGroupEnabled): ?>
              <select name="financial_statement_group_code" form="moduleBulkDeleteForm" data-bulk-statement-group-select>
                <option value=""><?= app_escape(app_t('Choose Financial Statement Group')) ?></option>
                <?php foreach ($bulkStatementGroupOptions as $optionValue => $optionLabel): ?>
                  <option value="<?= app_escape((string) $optionValue) ?>"><?= app_escape((string) $optionLabel) ?></option>
                <?php endforeach; ?>
              </select>
              <button class="btn btn-secondary" type="submit" form="moduleBulkDeleteForm" name="action" value="bulk_assign_statement_group" data-bulk-assign disabled><?= app_escape(app_t('Assign Group')) ?></button>
            <?php endif; ?>
            <?php if ($bulkDeleteEnabled): ?>
              <button class="btn btn-danger" type="submit" form="moduleBulkDeleteForm" name="action" value="bulk_delete" data-bulk-delete disabled><?= app_escape(app_t('Delete Selected')) ?></button>
            <?php endif; ?>
            <span class="bulk-count" data-bulk-count>0 <?= app_escape(app_t('selected')) ?></span>
          </div>
        <?php endif; ?>
      </div>

      <?php if (!$rows): ?>
        <div class="empty-state">No records found for this module and company context.</div>
      <?php else: ?>
        <?php if ($bulkSelectionEnabled): ?>
          <form id="moduleBulkDeleteForm" method="post" class="bulk-delete-form" data-bulk-delete-form data-confirm-message="<?= app_escape(app_t('Delete selected records?')) ?>" data-assign-confirm-message="<?= app_escape(app_t('Assign selected accounts to this financial statement group?')) ?>">
            <?= app_csrf_field() ?>
            <?php if ($companyId !== null): ?>
              <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
            <?php endif; ?>
          </form>
        <?php endif; ?>
        <div class="table-wrap">
          <table class="data-table">
            <thead>
              <tr>
                <?php if ($bulkSelectionEnabled): ?>
                  <th class="bulk-select-cell print-hide"><?= app_escape(app_t('Select')) ?></th>
                <?php endif; ?>
                <?php foreach ($listColumns as $column): ?>
                  <?php $columnLabel = (string) $column === 'company_access' ? app_t('Allowed Companies') : (string) $column; ?>
                  <th><?= app_escape($columnLabel) ?></th>
                <?php endforeach; ?>
                <th class="print-hide">Actions</th>
              </tr>
            </thead>
            <tbody>
              <?php foreach ($rows as $row): ?>
                <?php
                  $rowId = (int) ($row['id'] ?? 0);
                  $bulkDisabled = $slug === 'users' && $rowId === (int) (app_current_user()['id'] ?? 0);
                ?>
                <tr>
                  <?php if ($bulkSelectionEnabled): ?>
                    <td class="bulk-select-cell print-hide">
                      <input
                        type="checkbox"
                        name="record_ids[]"
                        value="<?= $rowId ?>"
                        form="moduleBulkDeleteForm"
                        data-bulk-checkbox
                        <?= $bulkDisabled ? 'disabled title="' . app_escape(app_t('You cannot delete the current logged-in user.')) . '"' : '' ?>
                      >
                    </td>
                  <?php endif; ?>
                  <?php foreach ($listColumns as $column): ?>
                    <td><?= module_cell_value($slug, (string) $column, $row[$column] ?? null, $definition, $companies, $companyId) ?></td>
                  <?php endforeach; ?>
                  <td class="print-hide">
                    <div class="actions">
                      <?php if ($slug === 'depreciation_runs'): ?>
                        <a class="icon-btn" href="<?= app_escape($moduleUrl(['edit' => (int) $row['id']])) ?>">View</a>
                        <?php
                          $journalEntryId = (int) ($row['__journal_entry_id'] ?? 0);
                          $journalEntryNumber = app_normalize_whitespace((string) ($row['__journal_entry_number'] ?? ''));
                        ?>
                        <?php if ($journalEntryId > 0 && $companyId !== null && $companyId > 0): ?>
                          <a class="icon-btn" href="<?= app_escape('module.php?module=journal_entries&view=' . $journalEntryId . '&company_id=' . (int) $companyId) ?>">
                            <?= app_escape($journalEntryNumber !== '' ? ('Journal ' . $journalEntryNumber) : 'Journal Entry') ?>
                          </a>
                        <?php endif; ?>
                        <?php
                          $runStatus = strtolower(app_normalize_whitespace((string) ($row['status'] ?? 'draft')));
                        ?>
                        <?php if ($canDelete && $runStatus !== 'posted'): ?>
                          <form method="post" onsubmit="return confirm('Delete this depreciation run?');">
                            <?= app_csrf_field() ?>
                            <input type="hidden" name="action" value="delete">
                            <input type="hidden" name="id" value="<?= (int) $row['id'] ?>">
                            <?php if ($companyId !== null): ?>
                              <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
                            <?php endif; ?>
                            <button class="icon-btn danger" type="submit">Delete</button>
                          </form>
                        <?php endif; ?>
                      <?php else: ?>
                        <?php if ($canEdit): ?>
                          <a class="icon-btn" href="<?= app_escape($moduleUrl(['edit' => (int) $row['id']])) ?>">Edit</a>
                        <?php endif; ?>
                        <?php if (in_array($slug, app_document_line_slugs(), true)): ?>
                          <a class="icon-btn" href="<?= app_escape($moduleUrl(['edit' => (int) $row['id']])) ?>">View</a>
                          <a class="icon-btn" href="<?= app_escape($moduleUrl(['edit' => (int) $row['id'], 'print' => '1'])) ?>">Print</a>
                        <?php endif; ?>
                        <?php if ($slug === 'sales_invoices' && $companyId !== null && $companyId > 0): ?>
                          <?php
                            $invoiceStatus = strtolower(app_normalize_whitespace((string) ($row['status'] ?? '')));
                            $invoiceRemaining = app_sales_invoice_remaining_amount($row);
                          ?>
                          <?php if ($invoiceRemaining > 0 && !in_array($invoiceStatus, ['draft', 'cancelled'], true)): ?>
                            <a class="icon-btn" href="<?= app_escape('module.php?module=payments&sales_invoice_id=' . (int) $row['id'] . '&company_id=' . (int) $companyId) ?>">Register Payment</a>
                          <?php endif; ?>
                        <?php endif; ?>
                        <?php if ($slug === 'purchase_bills' && $companyId !== null && $companyId > 0): ?>
                          <?php
                            $billStatus = strtolower(app_normalize_whitespace((string) ($row['status'] ?? '')));
                            $billRemaining = app_purchase_bill_remaining_amount($row);
                          ?>
                          <?php if ($billRemaining > 0 && !in_array($billStatus, ['draft', 'cancelled'], true)): ?>
                            <a class="icon-btn" href="<?= app_escape('module.php?module=payments&purchase_bill_id=' . (int) $row['id'] . '&company_id=' . (int) $companyId) ?>">Register Payment</a>
                          <?php endif; ?>
                        <?php endif; ?>
                        <?php if ($slug === 'expenses' && $companyId !== null && $companyId > 0): ?>
                          <?php
                            $expenseStatus = strtolower(app_normalize_whitespace((string) ($row['status'] ?? '')));
                            $expenseRemaining = app_expense_remaining_amount($row);
                          ?>
                          <?php if ($expenseRemaining > 0 && !in_array($expenseStatus, ['draft', 'cancelled'], true)): ?>
                            <a class="icon-btn" href="<?= app_escape('module.php?module=payments&expense_id=' . (int) $row['id'] . '&company_id=' . (int) $companyId) ?>">Register Payment</a>
                          <?php endif; ?>
                        <?php endif; ?>
                        <?php if ($slug === 'student_invoices' && $companyId !== null && $companyId > 0): ?>
                          <?php
                            $studentInvoiceStatus = strtolower(app_normalize_whitespace((string) ($row['status'] ?? '')));
                            $studentInvoiceRemaining = app_student_invoice_remaining_amount($row);
                          ?>
                          <?php if ($studentInvoiceRemaining > 0 && !in_array($studentInvoiceStatus, ['draft', 'cancelled'], true)): ?>
                            <a class="icon-btn" href="<?= app_escape('module.php?module=student_receipts&student_invoice_id=' . (int) $row['id'] . '&company_id=' . (int) $companyId) ?>">Register Receipt</a>
                          <?php endif; ?>
                        <?php endif; ?>
                        <?php if (app_module_requires_auto_posting($slug) && $companyId !== null && $companyId > 0): ?>
                          <?php
                            $journalEntryId = (int) ($row['__journal_entry_id'] ?? 0);
                            $journalEntryNumber = app_normalize_whitespace((string) ($row['__journal_entry_number'] ?? ''));
                          ?>
                          <?php if ($journalEntryId > 0): ?>
                            <a class="icon-btn" href="<?= app_escape('module.php?module=journal_entries&view=' . $journalEntryId . '&company_id=' . (int) $companyId) ?>">
                              <?= app_escape($journalEntryNumber !== '' ? ('Journal ' . $journalEntryNumber) : 'Journal Entry') ?>
                            </a>
                          <?php endif; ?>
                        <?php endif; ?>
                        <?php if ($slug === 'journal_entries'): ?>
                          <?php
                            $sourceType = app_normalize_whitespace((string) ($row['source_type'] ?? ''));
                            $sourceId = (int) ($row['source_id'] ?? 0);
                            $sourceUrl = app_source_module_url($sourceType, $sourceId, $companyId);
                          ?>
                          <?php if ($sourceUrl !== null): ?>
                            <a class="icon-btn" href="<?= app_escape($sourceUrl) ?>">Open Source</a>
                          <?php endif; ?>
                        <?php endif; ?>
                        <?php if ($canDelete): ?>
                          <form method="post" onsubmit="return confirm('Delete this record?');">
                            <?= app_csrf_field() ?>
                            <input type="hidden" name="action" value="delete">
                            <input type="hidden" name="id" value="<?= (int) $row['id'] ?>">
                            <?php if ($companyId !== null): ?>
                              <input type="hidden" name="company_id" value="<?= (int) $companyId ?>">
                            <?php endif; ?>
                            <button class="icon-btn danger" type="submit">Delete</button>
                          </form>
                        <?php endif; ?>
                      <?php endif; ?>
                    </div>
                  </td>
                </tr>
              <?php endforeach; ?>
            </tbody>
          </table>
        </div>
      <?php endif; ?>
    </section>
    <?php endif; ?>

    <?php if ($slug === 'depreciation_runs'): ?>
    <section class="table-card">
      <div class="table-header">
        <div>
          <h3>Depreciation Run Details</h3>
          <div class="hint">
            <?= $editing ? app_escape('Run ' . (string) ($editing['run_number'] ?? ('#' . $editId))) : 'Select a run from Records to view asset-level depreciation lines.' ?>
          </div>
        </div>
      </div>
      <?php if (!$editing): ?>
        <div class="empty-state">No run selected.</div>
      <?php elseif (!$depreciationRunLines): ?>
        <div class="empty-state">No depreciation lines available for this run.</div>
      <?php else: ?>
        <div class="panel" style="margin-bottom:12px;">
          <div class="form-grid four">
            <div class="field"><label>Run Date</label><input type="text" readonly value="<?= app_escape(app_date_format((string) ($editing['run_date'] ?? ''))) ?>"></div>
            <div class="field"><label>Cost Center</label><input type="text" readonly value="<?= app_escape((string) ($editing['cost_center_code'] ?? 'N/A')) ?>"></div>
            <div class="field"><label>Total Amount</label><input type="text" readonly value="<?= app_escape(app_money_format($editing['total_amount'] ?? 0)) ?>"></div>
            <div class="field"><label>Assets Count</label><input type="text" readonly value="<?= app_escape(number_format((int) ($editing['assets_count'] ?? 0))) ?>"></div>
          </div>
        </div>
        <div class="table-wrap">
          <table class="data-table" style="min-width: 1180px;">
            <thead>
              <tr>
                <th>Asset Code</th>
                <th>Asset Name</th>
                <th>Cost Center</th>
                <th>Previous Accumulated</th>
                <th>Depreciation Amount</th>
                <th>New Accumulated</th>
              </tr>
            </thead>
            <tbody>
              <?php foreach ($depreciationRunLines as $line): ?>
                <tr>
                  <td><?= app_escape((string) ($line['asset_code'] ?? '')) ?></td>
                  <td><?= app_escape((string) ($line['asset_name'] ?? '')) ?></td>
                  <td>
                    <?php
                      $lineCenterCode = app_normalize_whitespace((string) ($line['cost_center_code'] ?? ''));
                      $lineCenterName = app_normalize_whitespace((string) ($line['cost_center_name'] ?? ''));
                      $lineCenterLabel = $lineCenterCode !== ''
                          ? ($lineCenterCode . ($lineCenterName !== '' && $lineCenterName !== $lineCenterCode ? (' - ' . $lineCenterName) : ''))
                          : 'N/A';
                    ?>
                    <?= app_escape($lineCenterLabel) ?>
                  </td>
                  <td><?= app_escape(app_money_format($line['previous_accumulated'] ?? 0)) ?></td>
                  <td><?= app_escape(app_money_format($line['depreciation_amount'] ?? 0)) ?></td>
                  <td><?= app_escape(app_money_format($line['new_accumulated'] ?? 0)) ?></td>
                </tr>
              <?php endforeach; ?>
            </tbody>
          </table>
        </div>
      <?php endif; ?>
    </section>
    <?php endif; ?>

    <?php if ($slug === 'journal_entries'): ?>
      <section id="journalEntryPrintArea" class="table-card print-scope" data-print-scope data-default-print-target>
        <div class="table-header">
          <div>
            <h3>Journal Entry</h3>
            <div class="hint">
              <?= $journalDetailEntryId > 0 ? app_escape('Move #' . (string) $journalDetailEntryId . ' detailed items') : 'Select an entry to view detailed lines' ?>
            </div>
          </div>
          <div class="report-actions screen-only">
            <button class="btn btn-secondary" type="button" data-print-page data-print-target="#journalEntryPrintArea">Print Entry</button>
            <a class="btn btn-secondary" href="<?= app_escape($moduleExportUrl) ?>">Export XLSX</a>
          </div>
        </div>
        <?php if (!$journalDetailEntry || !$journalDetailLines): ?>
          <div class="empty-state">No detailed lines available for the selected journal entry.</div>
        <?php else: ?>
          <?php
            $entryStatus = app_normalize_whitespace((string) ($journalDetailEntry['status'] ?? 'draft'));
            $statusClass = match ($entryStatus) {
                'posted' => 'badge-green',
                'reversed' => 'badge-red',
                default => 'badge-yellow',
            };
            $sourceType = app_normalize_whitespace((string) ($journalDetailEntry['source_type'] ?? ''));
            $sourceId = (int) ($journalDetailEntry['source_id'] ?? 0);
            $sourceUrl = app_source_module_url($sourceType, $sourceId, $companyId);
            $totalDebit = 0.0;
            $totalCredit = 0.0;
            foreach ($journalDetailLines as $line) {
                $totalDebit += app_number($line['debit'] ?? 0);
                $totalCredit += app_number($line['credit'] ?? 0);
            }
            $difference = round($totalDebit - $totalCredit, 2);
          ?>
          <div class="odoo-move-card">
            <div class="odoo-move-head">
              <div>
                <div class="odoo-move-title"><?= app_escape((string) ($journalDetailEntry['entry_number'] ?? ('JE #' . $journalDetailEntryId))) ?></div>
                <div class="odoo-move-subtitle"><?= app_escape(app_date_format((string) ($journalDetailEntry['entry_date'] ?? ''))) ?><?= !empty($journalDetailEntry['reference']) ? (' &bull; Ref: ' . app_escape((string) $journalDetailEntry['reference'])) : '' ?></div>
              </div>
              <div class="odoo-move-head-actions">
                <span class="badge <?= app_escape($statusClass) ?>"><?= app_escape(ucfirst($entryStatus)) ?></span>
                <?php if ($sourceUrl !== null): ?>
                  <a class="icon-btn" href="<?= app_escape($sourceUrl) ?>">Open Source</a>
                <?php endif; ?>
              </div>
            </div>

            <div class="odoo-move-meta-grid">
              <div class="odoo-meta-item"><span>Move ID</span><strong><?= app_escape((string) $journalDetailEntryId) ?></strong></div>
              <div class="odoo-meta-item"><span>Source</span><strong><?= app_escape(app_source_module_label($sourceType)) ?></strong></div>
              <div class="odoo-meta-item"><span>Total Debit</span><strong><?= app_escape(app_money_format($totalDebit)) ?></strong></div>
              <div class="odoo-meta-item"><span>Total Credit</span><strong><?= app_escape(app_money_format($totalCredit)) ?></strong></div>
            </div>
          </div>

          <div class="table-wrap">
            <table class="data-table odoo-journal-table" style="min-width: 980px;">
              <thead>
                <tr>
                  <th>Line</th>
                  <th>Account Code</th>
                  <th>Account Name</th>
                  <th>Cost Center</th>
                  <th>Label (Description)</th>
                  <th>Debit</th>
                  <th>Credit</th>
                </tr>
              </thead>
              <tbody>
                <?php foreach ($journalDetailLines as $line): ?>
                  <tr>
                    <td><?= app_escape((string) ($line['line_no'] ?? '')) ?></td>
                    <td><?= app_escape((string) ($line['account_code'] ?? '')) ?></td>
                    <td><?= app_escape((string) ($line['account_name'] ?? '')) ?></td>
                    <td>
                      <?php
                        $costCenterCode = app_normalize_whitespace((string) ($line['cost_center_code'] ?? ''));
                        $costCenterName = app_normalize_whitespace((string) ($line['cost_center_name'] ?? ''));
                        $costCenterLabel = $costCenterCode !== ''
                            ? ($costCenterCode . ($costCenterName !== '' && $costCenterName !== $costCenterCode ? (' - ' . $costCenterName) : ''))
                            : 'N/A';
                      ?>
                      <?= app_escape($costCenterLabel) ?>
                    </td>
                    <td><?= app_escape((string) ($line['description'] ?? '')) ?></td>
                    <td><?= app_escape(app_money_format($line['debit'] ?? 0)) ?></td>
                    <td><?= app_escape(app_money_format($line['credit'] ?? 0)) ?></td>
                  </tr>
                <?php endforeach; ?>
              </tbody>
              <tfoot>
                <tr>
                  <th colspan="5">Totals</th>
                  <th><?= app_escape(app_money_format($totalDebit)) ?></th>
                  <th><?= app_escape(app_money_format($totalCredit)) ?></th>
                </tr>
                <tr>
                  <th colspan="5">Difference</th>
                  <th colspan="2">
                    <?php if (abs($difference) < 0.01): ?>
                      <span class="badge badge-green">Balanced</span>
                    <?php else: ?>
                      <span class="badge badge-red">Unbalanced: <?= app_escape(app_money_format(abs($difference))) ?></span>
                    <?php endif; ?>
                  </th>
                </tr>
              </tfoot>
            </table>
          </div>
        <?php endif; ?>
      </section>
    <?php endif; ?>

    <div class="footer">
      <?= app_escape((string) app_config('app_name')) ?> &bull; <?= app_escape(app_t('Module')) ?>: <?= app_escape($title) ?>
    </div>
  </div>

  <button id="floatingPrintBtn" class="floating-print-btn screen-only" type="button">Print</button>
</div>
<?php if (in_array($slug, ['sales_invoices', 'student_invoices', 'sales_returns'], true)): ?>
<script src="assets/qrcode.min.js"></script>
<?php endif; ?>
<script src="assets/app.js"></script>
<?php if ($slug === 'journal_entries' && $journalPrintRequested): ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
  if (window.orionPrintTarget) {
    window.orionPrintTarget('#journalEntryPrintArea');
  } else {
    window.print();
  }
});
</script>
<?php endif; ?>
<?php if ($invoicePrintRequested): ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
  if (window.orionPrintTarget) {
    window.orionPrintTarget('#invoicePrintArea');
  } else {
    window.print();
  }
});
</script>
<?php endif; ?>
<?php if (in_array($slug, ['sales_invoices', 'student_invoices', 'purchase_bills', 'expenses', 'prepaid_expenses'], true)): ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
  if (document.querySelector('[data-document-lines-editor]')) return;
  const isPurchase = <?= in_array($slug, ['purchase_bills', 'expenses', 'prepaid_expenses'], true) ? 'true' : 'false' ?>;
  const isPurchaseBill = <?= $slug === 'purchase_bills' ? 'true' : 'false' ?>;
  const isExpense = <?= $slug === 'expenses' ? 'true' : 'false' ?>;
  const isPrepaidExpense = <?= $slug === 'prepaid_expenses' ? 'true' : 'false' ?>;
  const taxRates = <?= json_encode($invoiceTaxRateMap, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?: '{}' ?>;
  const withholdingTaxRates = <?= json_encode($invoiceWithholdingTaxRateMap, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?: '{}' ?>;
  const vendorWithholding = <?= json_encode($vendorWithholdingMap, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT) ?: '{}' ?>;
  const taxableEl = document.getElementById('is_taxable');
  const vendorEl = document.getElementById('vendor_code');
  const taxNameEl = document.getElementById('tax_name');
  const taxRateEl = document.getElementById('tax_rate_percent');
  const withholdingNameEl = document.getElementById('withholding_tax_name');
  const withholdingRateEl = document.getElementById('withholding_tax_rate_percent');
  const withholdingTotalEl = document.getElementById('withholding_tax_total');
  const netPayableEl = document.getElementById('net_payable');
  const subtotalEl = document.getElementById(isPrepaidExpense ? 'total_amount' : 'subtotal');
  const discountEl = document.getElementById('discount_total');
  const taxEl = document.getElementById('tax_total');
  const grandEl = document.getElementById('grand_total');
  const paidEl = document.getElementById('inline_paid_amount') || document.getElementById('paid_amount');
  const statusEl = document.getElementById('status');
  const paymentMethodEl = document.getElementById('payment_method');
  const paymentAccountEl = document.getElementById('payment_account_name');
  const offsetAccountEl = document.getElementById('offset_account_code');
  const summaryBaseEl = document.querySelector('[data-tax-summary-base]');
  const summaryTaxEl = document.querySelector('[data-tax-summary-tax]');
  const summaryGrandEl = document.querySelector('[data-tax-summary-grand]');
  const summaryWithholdingEl = document.querySelector('[data-tax-summary-withholding]');
  const summaryNetPayableEl = document.querySelector('[data-tax-summary-net-payable]');

  const toNumber = (value) => {
    const normalized = String(value ?? '')
      .replace(/[٠-٩]/g, (digit) => '٠١٢٣٤٥٦٧٨٩'.indexOf(digit))
      .replace(/[۰-۹]/g, (digit) => '۰۱۲۳۴۵۶۷۸۹'.indexOf(digit))
      .replace(/,/g, '')
      .trim();
    const parsed = Number(normalized);
    return Number.isFinite(parsed) ? parsed : 0;
  };

  const toFixed2 = (value) => (Math.round(value * 100) / 100).toFixed(2);

  const taxRateValues = Object.values(taxRates)
    .map((value) => Math.max(0, toNumber(value)))
    .filter((value) => value > 0);
  const defaultTaxRate = taxRateValues.length ? taxRateValues[0] : 15;

  const selectedTaxRate = () => {
    if (!taxNameEl || !taxNameEl.value) return null;
    const option = taxNameEl.options[taxNameEl.selectedIndex] || null;
    const dataRate = option && option.dataset ? option.dataset.taxRate : undefined;
    if (dataRate !== undefined && dataRate !== '') {
      return Math.max(0, toNumber(dataRate));
    }
    if (Object.prototype.hasOwnProperty.call(taxRates, taxNameEl.value)) {
      return Math.max(0, toNumber(taxRates[taxNameEl.value]));
    }
    const selectedText = option ? option.text : '';
    const match = selectedText.match(/([\d٠-٩۰-۹.]+)\s*%|%\s*([\d٠-٩۰-۹.]+)/);
    return match && (match[1] || match[2]) ? Math.max(0, toNumber(match[1] || match[2])) : null;
  };

  const selectedWithholdingRate = () => {
    if (!withholdingNameEl || !withholdingNameEl.value) return 0;
    const option = withholdingNameEl.options[withholdingNameEl.selectedIndex] || null;
    const dataRate = option && option.dataset ? option.dataset.taxRate : undefined;
    if (dataRate !== undefined && dataRate !== '') {
      return Math.max(0, toNumber(dataRate));
    }
    if (Object.prototype.hasOwnProperty.call(withholdingTaxRates, withholdingNameEl.value)) {
      return Math.max(0, toNumber(withholdingTaxRates[withholdingNameEl.value]));
    }
    const selectedText = option ? option.text : '';
    const match = selectedText.match(/([\dظ -ظ©غ°-غ¹.]+)\s*%|%\s*([\dظ -ظ©غ°-غ¹.]+)/);
    return match && (match[1] || match[2]) ? Math.max(0, toNumber(match[1] || match[2])) : 0;
  };

  const applyVendorWithholding = () => {
    if (!isPurchaseBill || !vendorEl || !withholdingNameEl) return;
    const vendorMeta = vendorWithholding[vendorEl.value] || null;
    if (!vendorMeta || !vendorMeta.tax_name) return;
    if (!withholdingNameEl.value) {
      withholdingNameEl.value = vendorMeta.tax_name;
    }
    if (withholdingRateEl) {
      withholdingRateEl.value = toFixed2(Math.max(0, toNumber(vendorMeta.rate)));
    }
  };

  const updateStatus = () => {
    if (!statusEl || !grandEl || !paidEl) return;
    const current = String(statusEl.value || '').toLowerCase();
    if (current === 'draft' || current === 'cancelled') return;

    const grand = Math.max(0, toNumber(grandEl.value));
    const paymentBasis = isPurchaseBill && netPayableEl ? Math.max(0, toNumber(netPayableEl.value)) : grand;
    let paid = Math.max(0, toNumber(paidEl.value));
    if (paid > paymentBasis) {
      paid = paymentBasis;
      paidEl.value = toFixed2(paid);
    }

    if (isPrepaidExpense) return;

    if (paid <= 0 || paymentBasis <= 0) {
      statusEl.value = isPurchase ? 'approved' : 'issued';
    } else if (paid >= (paymentBasis - 0.01)) {
      statusEl.value = 'paid';
    } else {
      statusEl.value = 'partially_paid';
    }
  };

  const updatePaymentFields = () => {
    if (!paidEl) return;
    const paid = Math.max(0, toNumber(paidEl.value));
    const hasPaid = paid > 0;
    if (paymentMethodEl) {
      paymentMethodEl.required = hasPaid;
      if (!hasPaid) paymentMethodEl.value = '';
    }
    if (paymentAccountEl) {
      paymentAccountEl.required = hasPaid;
      if (!hasPaid) paymentAccountEl.value = '';
    }
  };

  const updateExpenseOffsetField = () => {
    if (!isExpense || !offsetAccountEl || !grandEl || !paidEl) return;
    const grand = Math.max(0, toNumber(grandEl.value));
    let paid = Math.max(0, toNumber(paidEl.value));
    if (paid > grand) {
      paid = grand;
    }
    const remaining = Math.max(0, grand - paid);
    const requiresOffset = remaining > 0.01;
    const fieldEl = offsetAccountEl.closest('.field');
    if (fieldEl) {
      fieldEl.style.display = requiresOffset ? '' : 'none';
    }
    offsetAccountEl.required = requiresOffset;
    if (!requiresOffset) {
      offsetAccountEl.value = '';
    }
  };

  const recalc = () => {
    if (!subtotalEl || !taxEl || !grandEl) return;

    const subtotal = Math.max(0, toNumber(subtotalEl.value));
    const discount = Math.max(0, toNumber(discountEl ? discountEl.value : 0));
    const base = Math.max(0, subtotal - discount);
    const isTaxable = taxableEl ? taxableEl.checked : false;
    let taxRate = Math.max(0, toNumber(taxRateEl ? taxRateEl.value : 0));
    const mappedTaxRate = selectedTaxRate();

    if (isTaxable && mappedTaxRate !== null) {
      taxRate = mappedTaxRate;
      if (taxRateEl) taxRateEl.value = toFixed2(taxRate);
    }

    if (isTaxable && mappedTaxRate === null && taxRate <= 0) {
      taxRate = defaultTaxRate;
      if (taxRateEl) taxRateEl.value = toFixed2(taxRate);
    }
    if (!isTaxable) {
      taxRate = 0;
      if (taxRateEl) taxRateEl.value = toFixed2(0);
    }

    const tax = isTaxable ? (base * taxRate / 100) : 0;
    const grand = base + tax;
    let withholdingTax = 0;
    let netPayable = grand;
    if (isPurchaseBill && withholdingNameEl && withholdingTotalEl && netPayableEl) {
      applyVendorWithholding();
      const withholdingRate = selectedWithholdingRate();
      if (withholdingRateEl) withholdingRateEl.value = toFixed2(withholdingRate);
      withholdingTax = withholdingNameEl.value ? Math.min(grand, base * withholdingRate / 100) : 0;
      netPayable = Math.max(0, grand - withholdingTax);
      withholdingTotalEl.value = toFixed2(withholdingTax);
      netPayableEl.value = toFixed2(netPayable);
      withholdingTotalEl.readOnly = true;
      netPayableEl.readOnly = true;
      if (summaryWithholdingEl) summaryWithholdingEl.textContent = toFixed2(withholdingTax);
      if (summaryNetPayableEl) summaryNetPayableEl.textContent = toFixed2(netPayable);
    }
    if (!isPurchaseBill) {
      if (summaryWithholdingEl) summaryWithholdingEl.textContent = toFixed2(0);
      if (summaryNetPayableEl) summaryNetPayableEl.textContent = toFixed2(grand);
    }

    taxEl.value = toFixed2(tax);
    grandEl.value = toFixed2(grand);
    if (summaryBaseEl) summaryBaseEl.textContent = toFixed2(base);
    if (summaryTaxEl) summaryTaxEl.textContent = toFixed2(tax);
    if (summaryGrandEl) summaryGrandEl.textContent = toFixed2(grand);
    taxEl.readOnly = true;
    grandEl.readOnly = true;
    if (taxRateEl) {
      taxRateEl.readOnly = !isTaxable;
    }

    updateStatus();
    updatePaymentFields();
    updateExpenseOffsetField();
  };

  [taxableEl, taxNameEl, taxRateEl, withholdingNameEl, withholdingRateEl, vendorEl, subtotalEl, discountEl, paidEl, statusEl].forEach((el) => {
    if (!el) return;
    el.addEventListener('change', recalc);
    el.addEventListener('input', recalc);
  });

  recalc();
});
</script>
<?php endif; ?>
<?php if ($slug === 'financial_statement_groups'): ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
  const statementTypeEl = document.getElementById('statement_type');
  const mainSectionEl = document.getElementById('main_section');
  if (!statementTypeEl || !mainSectionEl) return;
  const sectionType = {
    bs_non_current_assets: 'balance_sheet',
    bs_current_assets: 'balance_sheet',
    bs_equity: 'balance_sheet',
    bs_non_current_liabilities: 'balance_sheet',
    bs_current_liabilities: 'balance_sheet',
    is_revenue: 'income_statement',
    is_revenue_deductions: 'income_statement',
    is_cost_of_revenue: 'income_statement',
    is_operating_expenses: 'income_statement',
    is_other_income: 'income_statement',
    is_finance_costs: 'income_statement',
    is_income_tax: 'income_statement',
  };
  const refreshSections = () => {
    const statementType = String(statementTypeEl.value || '').trim();
    const selected = mainSectionEl.options[mainSectionEl.selectedIndex] || null;
    Array.from(mainSectionEl.options).forEach((option) => {
      if (!option.value) {
        option.disabled = false;
        option.hidden = false;
        return;
      }
      const hidden = statementType !== '' && sectionType[option.value] && sectionType[option.value] !== statementType;
      option.disabled = hidden;
      option.hidden = hidden;
    });
    if (selected && selected.disabled) {
      mainSectionEl.value = '';
    }
  };
  statementTypeEl.addEventListener('change', refreshSections);
  refreshSections();
});
</script>
<?php endif; ?>
<?php if ($slug === 'payments'): ?>
<script>
document.addEventListener('DOMContentLoaded', () => {
  const salesInvoiceEl = document.getElementById('sales_invoice_id');
  const billEl = document.getElementById('purchase_bill_id');
  const expenseEl = document.getElementById('expense_id');
  const ifrsLeaseEl = document.getElementById('ifrs16_lease_id');
  const ifrsScheduleEl = document.getElementById('ifrs16_schedule_id');
  const directionEl = document.getElementById('direction');
  const referenceEl = document.getElementById('reference');
  const contactEl = document.getElementById('contact_code');
  const amountEl = document.getElementById('amount');
  if (!salesInvoiceEl && !billEl && !expenseEl && !ifrsLeaseEl && !ifrsScheduleEl) return;

  const formatAmount = (value) => {
    const parsed = Number(String(value ?? '').replace(/,/g, '').trim());
    return Number.isFinite(parsed) ? (Math.round(parsed * 100) / 100).toFixed(2) : '';
  };
  const parseAmount = (value) => {
    const parsed = Number(String(value ?? '').replace(/,/g, '').trim());
    return Number.isFinite(parsed) ? parsed : 0;
  };

  const clearLinkedSelect = (select) => {
    if (!select || !select.value) return;
    select.value = '';
    select.dispatchEvent(new Event('change', { bubbles: true }));
  };
  const setSelectValue = (select, value) => {
    if (!select) return;
    const nextValue = String(value ?? '');
    if (select.value === nextValue) return;
    select.value = nextValue;
    select.dispatchEvent(new Event('change', { bubbles: true }));
  };
  const clearIfrsAmountLimit = () => {
    if (!amountEl) return;
    amountEl.removeAttribute('max');
    amountEl.setCustomValidity('');
  };

  const selectedIfrsScheduleOption = () => {
    if (!ifrsScheduleEl || !ifrsScheduleEl.value) return null;
    return ifrsScheduleEl.options[ifrsScheduleEl.selectedIndex] || null;
  };
  const selectedIfrsRemaining = () => {
    const option = selectedIfrsScheduleOption();
    if (!option || typeof option.dataset.remaining === 'undefined') return null;
    const remaining = parseAmount(option.dataset.remaining);
    return Number.isFinite(remaining) ? Math.max(0, remaining) : null;
  };
  const applyIfrsAmountLimit = () => {
    if (!amountEl) return;
    const remaining = selectedIfrsRemaining();
    if (remaining === null) {
      clearIfrsAmountLimit();
      return;
    }
    const formattedRemaining = formatAmount(remaining);
    amountEl.max = formattedRemaining;
    if (parseAmount(amountEl.value) > remaining) {
      amountEl.value = formattedRemaining;
    }
    amountEl.setCustomValidity('');
  };

  const refreshSalesInvoiceOptions = () => {
    if (!salesInvoiceEl || !contactEl) return;
    const contactCode = String(contactEl.value || '').trim();
    const selectedOption = salesInvoiceEl.options[salesInvoiceEl.selectedIndex] || null;
    Array.from(salesInvoiceEl.options).forEach((option) => {
      if (!option.value) {
        option.disabled = false;
        option.hidden = false;
        return;
      }
      const optionCustomer = String(option.dataset.customerCode || '').trim();
      const hidden = contactCode !== '' && optionCustomer !== '' && optionCustomer !== contactCode;
      option.disabled = hidden;
      option.hidden = hidden;
    });
    if (selectedOption && selectedOption.disabled) {
      clearLinkedSelect(salesInvoiceEl);
    }
  };
  const refreshIfrsScheduleOptions = () => {
    if (!ifrsScheduleEl) return;
    const leaseId = String(ifrsLeaseEl?.value || '').trim();
    const selectedOption = ifrsScheduleEl.options[ifrsScheduleEl.selectedIndex] || null;
    Array.from(ifrsScheduleEl.options).forEach((option) => {
      if (!option.value) {
        option.disabled = false;
        option.hidden = false;
        return;
      }
      const optionLeaseId = String(option.dataset.leaseId || '').trim();
      const hidden = leaseId !== '' && optionLeaseId !== '' && optionLeaseId !== leaseId;
      option.disabled = hidden;
      option.hidden = hidden;
    });
    if (selectedOption && selectedOption.disabled) {
      clearLinkedSelect(ifrsScheduleEl);
    } else {
      applyIfrsAmountLimit();
    }
  };

  contactEl?.addEventListener('change', refreshSalesInvoiceOptions);

  salesInvoiceEl?.addEventListener('change', () => {
    const option = salesInvoiceEl.options[salesInvoiceEl.selectedIndex];
    if (!option || !salesInvoiceEl.value) return;
    clearLinkedSelect(billEl);
    clearLinkedSelect(expenseEl);
    clearLinkedSelect(ifrsLeaseEl);
    clearLinkedSelect(ifrsScheduleEl);
    clearIfrsAmountLimit();
    setSelectValue(directionEl, 'in');
    if (referenceEl && option.dataset.invoiceNumber) referenceEl.value = option.dataset.invoiceNumber;
    if (option.dataset.customerCode) setSelectValue(contactEl, option.dataset.customerCode);
    if (amountEl && option.dataset.remaining) amountEl.value = formatAmount(option.dataset.remaining);
    refreshSalesInvoiceOptions();
  });

  billEl?.addEventListener('change', () => {
    const option = billEl.options[billEl.selectedIndex];
    if (!option || !billEl.value) return;
    clearLinkedSelect(salesInvoiceEl);
    clearLinkedSelect(expenseEl);
    clearLinkedSelect(ifrsLeaseEl);
    clearLinkedSelect(ifrsScheduleEl);
    clearIfrsAmountLimit();
    setSelectValue(directionEl, 'out');
    if (referenceEl && option.dataset.billNumber) referenceEl.value = option.dataset.billNumber;
    if (option.dataset.vendorCode) setSelectValue(contactEl, option.dataset.vendorCode);
    if (amountEl && option.dataset.remaining) amountEl.value = formatAmount(option.dataset.remaining);
  });

  expenseEl?.addEventListener('change', () => {
    const option = expenseEl.options[expenseEl.selectedIndex];
    if (!option || !expenseEl.value) return;
    clearLinkedSelect(salesInvoiceEl);
    clearLinkedSelect(billEl);
    clearLinkedSelect(ifrsLeaseEl);
    clearLinkedSelect(ifrsScheduleEl);
    clearIfrsAmountLimit();
    setSelectValue(directionEl, 'out');
    if (referenceEl && option.dataset.expenseNumber) referenceEl.value = option.dataset.expenseNumber;
    if (option.dataset.payeeCode) setSelectValue(contactEl, option.dataset.payeeCode);
    if (amountEl && option.dataset.remaining) amountEl.value = formatAmount(option.dataset.remaining);
  });
  ifrsLeaseEl?.addEventListener('change', () => {
    refreshIfrsScheduleOptions();
    if (!ifrsLeaseEl.value) return;
    clearLinkedSelect(salesInvoiceEl);
    clearLinkedSelect(billEl);
    clearLinkedSelect(expenseEl);
    setSelectValue(directionEl, 'out');
  });
  ifrsScheduleEl?.addEventListener('change', () => {
    const option = selectedIfrsScheduleOption();
    if (!option) {
      applyIfrsAmountLimit();
      return;
    }
    const selectedScheduleId = ifrsScheduleEl.value;
    clearLinkedSelect(salesInvoiceEl);
    clearLinkedSelect(billEl);
    clearLinkedSelect(expenseEl);
    if (option.dataset.leaseId) {
      setSelectValue(ifrsLeaseEl, option.dataset.leaseId);
      if (ifrsScheduleEl) ifrsScheduleEl.value = selectedScheduleId;
    }
    setSelectValue(directionEl, 'out');
    if (referenceEl) {
      const parts = [];
      if (option.dataset.leaseNumber) parts.push(option.dataset.leaseNumber);
      const sequence = Number(option.dataset.sequenceNo || 0);
      if (sequence > 0) parts.push(`#${String(sequence).padStart(3, '0')}`);
      if (option.dataset.scheduleDate) parts.push(option.dataset.scheduleDate);
      if (parts.length > 0) referenceEl.value = parts.join(' - ');
    }
    if (option.dataset.lessorCode) setSelectValue(contactEl, option.dataset.lessorCode);
    const remaining = selectedIfrsRemaining();
    if (amountEl && remaining !== null) amountEl.value = formatAmount(remaining);
    applyIfrsAmountLimit();
  });
  amountEl?.addEventListener('input', applyIfrsAmountLimit);
  amountEl?.addEventListener('change', applyIfrsAmountLimit);
  refreshSalesInvoiceOptions();
  refreshIfrsScheduleOptions();
  applyIfrsAmountLimit();
});
</script>
<?php endif; ?>
</body>
</html>
