diff --git a/app/Config/HrLoginDiagnoseRoutes.php b/app/Config/HrLoginDiagnoseRoutes.php
new file mode 100644
index 00000000..b02f0c38
--- /dev/null
+++ b/app/Config/HrLoginDiagnoseRoutes.php
@@ -0,0 +1,18 @@
+group('hrLoginDiagnose', ['filter' => 'authMVC'], static function ($routes) {
+ $routes->get('/', 'HrLoginDiagnoseController::index');
+ $routes->get('', 'HrLoginDiagnoseController::index');
+ $routes->post('check', 'HrLoginDiagnoseController::check');
+});
+
+// Fallback when request path still includes index.php (wrong baseURL / form action).
+$routes->get('index.php/hrLoginDiagnose', 'HrLoginDiagnoseController::index', ['filter' => 'authMVC']);
+$routes->get('index.php/hrLoginDiagnose/', 'HrLoginDiagnoseController::index', ['filter' => 'authMVC']);
+$routes->post('index.php/hrLoginDiagnose/check', 'HrLoginDiagnoseController::check', ['filter' => 'authMVC']);
diff --git a/app/Config/Routing.php b/app/Config/Routing.php
index 8d3c7731..eda3998d 100755
--- a/app/Config/Routing.php
+++ b/app/Config/Routing.php
@@ -27,6 +27,7 @@ class Routing extends BaseRouting
*/
public array $routeFiles = [
APPPATH . 'Config/Routes.php',
+ APPPATH . 'Config/HrLoginDiagnoseRoutes.php',
];
/**
diff --git a/app/Controllers/HrLoginDiagnoseController.php b/app/Controllers/HrLoginDiagnoseController.php
new file mode 100644
index 00000000..0b7a1fa3
--- /dev/null
+++ b/app/Controllers/HrLoginDiagnoseController.php
@@ -0,0 +1,526 @@
+request->getGet('email'));
+ $mobile = trim((string) $this->request->getGet('mobile'));
+
+ $data = [
+ 'title' => 'HR Login Diagnostic',
+ 'email' => $email,
+ 'mobile' => $mobile,
+ 'result' => null,
+ 'has_search' => false,
+ ];
+
+ if ($email !== '' || $mobile !== '') {
+ $data['has_search'] = true;
+ $data['result'] = $this->diagnose($email, $mobile);
+ }
+
+ return view('hr_login_diagnose', $data);
+ }
+
+ public function check()
+ {
+ try {
+ $payload = $this->request->getJSON(true);
+ if (!is_array($payload) || empty($payload)) {
+ $payload = [
+ 'email' => $this->request->getPost('email'),
+ 'mobile' => $this->request->getPost('mobile'),
+ ];
+ }
+
+ $email = trim((string) ($payload['email'] ?? ''));
+ $mobile = trim((string) ($payload['mobile'] ?? ''));
+
+ if ($email === '' && $mobile === '') {
+ return $this->respond([
+ 'status' => false,
+ 'message' => 'Enter email or mobile.',
+ ], 400);
+ }
+
+ $result = $this->diagnose($email, $mobile);
+
+ return $this->respond([
+ 'status' => true,
+ 'data' => $result,
+ ], 200);
+ } catch (\Throwable $e) {
+ log_message('error', 'HrLoginDiagnose check failed: ' . $e->getMessage() . ' @ ' . $e->getFile() . ':' . $e->getLine());
+
+ return $this->respond([
+ 'status' => false,
+ 'message' => 'Diagnosis failed: ' . $e->getMessage(),
+ ], 500);
+ }
+ }
+
+ private function diagnose(string $email, string $mobile): array
+ {
+ $issues = [];
+ $postDb = \Config\Database::connect();
+ $contacts = $this->fetchPostContacts($postDb, $email, $mobile);
+
+ if (empty($contacts)) {
+ $issues[] = $this->issue(
+ 'HR_NOT_FOUND',
+ 'high',
+ 'No active HR found in post level_contacts for the given email/mobile (contact_type=client).',
+ 'Create/activate the HR contact under the correct client branch, or verify the email/mobile.'
+ );
+
+ return [
+ 'input' => ['email' => $email, 'mobile' => $mobile],
+ 'overall_status' => 'FAIL',
+ 'summary' => 'HR not found in post DB.',
+ 'issues' => $issues,
+ 'post' => ['contacts' => []],
+ 'hr_access_control'=> [],
+ 'pre' => ['reachable' => null, 'mappings' => []],
+ 'login_simulation' => [
+ 'would_authenticate' => false,
+ 'cards' => [],
+ ],
+ ];
+ }
+
+ $accessRows = [];
+ $preMaps = [];
+ $loginCards = [];
+ $preReachable = null;
+
+ try {
+ $preDb = \Config\Database::connect('preDB');
+ $preDb->connect();
+ $preReachable = true;
+ } catch (\Throwable $e) {
+ $preReachable = false;
+ $preDb = null;
+ $issues[] = $this->issue(
+ 'PRE_DB_UNREACHABLE',
+ 'high',
+ 'Could not connect to preDB: ' . $e->getMessage(),
+ 'Check preDB credentials in Database config / .env.'
+ );
+ }
+
+ foreach ($contacts as $contact) {
+ $flags = [
+ 'HR_FOUND' => true,
+ 'BRANCH_ACTIVE' => (int) ($contact['branch_active'] ?? 0) === 1,
+ 'CLIENT_ACTIVE' => (int) ($contact['client_active'] ?? 0) === 1,
+ ];
+
+ if (!$flags['BRANCH_ACTIVE']) {
+ $issues[] = $this->issue(
+ 'BRANCH_INACTIVE',
+ 'high',
+ "Branch inactive for post_hr_id={$contact['post_hr_id']} (branch #{$contact['post_branch_id']}).",
+ 'Activate client_branch.is_active=1 for this branch.'
+ );
+ }
+ if (!$flags['CLIENT_ACTIVE']) {
+ $issues[] = $this->issue(
+ 'CLIENT_INACTIVE',
+ 'high',
+ "Client inactive for post_hr_id={$contact['post_hr_id']} (client #{$contact['post_client_id']}).",
+ 'Activate clients.is_active=1 for this client.'
+ );
+ }
+
+ $access = $this->fetchAccessRow(
+ $postDb,
+ (int) $contact['post_hr_id'],
+ (int) $contact['post_branch_id'],
+ (int) $contact['post_client_id']
+ );
+
+ $modules = [];
+ $hasModules = false;
+ $tokenReady = false;
+ $failureReason = null;
+
+ if (empty($access)) {
+ $failureReason = 'NO_HR_ACCESS_CONTROL_ROW';
+ $issues[] = $this->issue(
+ 'NO_HR_ACCESS_CONTROL_ROW',
+ 'high',
+ "No hr_access_control row for post_hr_id={$contact['post_hr_id']}, post_branch_id={$contact['post_branch_id']}, post_client_id={$contact['post_client_id']}.",
+ 'Open Client Onboarding → HR Access and save at least one module for this HR/branch.'
+ );
+ } else {
+ $modules = json_decode($access['allowed_modules'] ?? '[]', true);
+ if (!is_array($modules)) {
+ $modules = [];
+ }
+ $hasModules = $this->hasAnyModule($modules);
+ $tokenReady = $hasModules;
+
+ if (!$hasModules) {
+ $failureReason = 'EMPTY_ALLOWED_MODULES';
+ $issues[] = $this->issue(
+ 'EMPTY_ALLOWED_MODULES',
+ 'high',
+ "hr_access_control id={$access['id']} exists but allowed_modules is empty.",
+ 'Assign pre/post modules in HR Access Control and save.'
+ );
+ }
+ }
+
+ $accessRows[] = [
+ 'id' => $access['id'] ?? null,
+ 'post_hr_id' => (int) $contact['post_hr_id'],
+ 'pre_hr_id' => isset($access['pre_hr_id']) ? (int) $access['pre_hr_id'] : null,
+ 'post_branch_id' => (int) $contact['post_branch_id'],
+ 'pre_branch_id' => isset($access['pre_branch_id']) ? (int) $access['pre_branch_id'] : null,
+ 'post_client_id' => (int) $contact['post_client_id'],
+ 'pre_client_id' => isset($access['pre_client_id']) ? (int) $access['pre_client_id'] : null,
+ 'allowed_modules' => $modules,
+ 'allowed_pre_policies' => json_decode($access['allowed_pre_policies'] ?? '[]', true) ?: [],
+ 'allowed_active_policies' => json_decode($access['allowed_active_policies'] ?? '[]', true) ?: [],
+ 'allowed_cd' => json_decode($access['allowed_cd'] ?? '[]', true) ?: [],
+ 'is_active' => isset($access['is_active']) ? (int) $access['is_active'] : null,
+ 'has_token' => $tokenReady,
+ 'token_ready' => $tokenReady,
+ 'failure_reason' => $failureReason,
+ 'note' => $failureReason ?: 'OK',
+ ];
+
+ $preMap = $this->buildPreMapping($preDb, $contact, $access);
+ $preMaps[] = $preMap;
+
+ if ($preReachable === true) {
+ if (empty($preMap['PRE_BRANCH_LINKED'])) {
+ $issues[] = $this->issue(
+ 'PRE_BRANCH_NOT_LINKED',
+ 'medium',
+ "Post branch #{$contact['post_branch_id']} has no valid pre_branch_id link.",
+ 'Set client_branch.pre_branch_id to the matching pre branch id.'
+ );
+ } elseif (empty($preMap['PRE_HR_MATCHED'])) {
+ $issues[] = $this->issue(
+ 'PRE_HR_NOT_MATCHED',
+ 'medium',
+ "No pre level_contacts match for email+mobile under pre client #{$preMap['pre_client_id']}.",
+ 'Align email and mobile between pre and post HR contacts.'
+ );
+ } elseif (empty($preMap['PRE_IDS_CONSISTENT']) && !empty($access)) {
+ $issues[] = $this->issue(
+ 'PRE_IDS_INCONSISTENT',
+ 'medium',
+ "hr_access_control pre ids do not match live pre data for post_hr_id={$contact['post_hr_id']}.",
+ 'Re-save HR Access Control so pre_hr_id / pre_client_id / pre_branch_id are refreshed.'
+ );
+ }
+ }
+
+ $canLoginCard = $flags['BRANCH_ACTIVE'] && $flags['CLIENT_ACTIVE'] && $tokenReady;
+ $loginCards[] = [
+ 'client' => $contact['client_name'] ?? '',
+ 'branch' => $contact['branch_name'] ?? '',
+ 'post_hr_id' => (int) $contact['post_hr_id'],
+ 'post_client_id' => (int) $contact['post_client_id'],
+ 'post_branch_id' => (int) $contact['post_branch_id'],
+ 'has_token' => $canLoginCard,
+ 'failure_reason' => $canLoginCard
+ ? null
+ : ($failureReason
+ ?: (!$flags['BRANCH_ACTIVE'] ? 'BRANCH_INACTIVE'
+ : (!$flags['CLIENT_ACTIVE'] ? 'CLIENT_INACTIVE' : 'UNKNOWN'))),
+ ];
+
+ $contact['hr_found'] = true;
+ $contact['flags'] = $flags;
+ }
+
+ $anyToken = false;
+ foreach ($loginCards as $card) {
+ if (!empty($card['has_token'])) {
+ $anyToken = true;
+ break;
+ }
+ }
+
+ $wouldAuth = !empty($contacts);
+ $overall = ($wouldAuth && $anyToken && empty(array_filter($issues, static fn ($i) => ($i['severity'] ?? '') === 'high')))
+ ? 'PASS'
+ : (($anyToken && $wouldAuth) ? 'WARN' : 'FAIL');
+
+ if ($overall === 'PASS') {
+ $summary = 'HR identity, access row, and login readiness look OK.';
+ $verdict = 'OK';
+ } elseif ($overall === 'WARN') {
+ $summary = 'Login may work for some cards, but mapping/access issues remain.';
+ $verdict = 'OK_WITH_WARNINGS';
+ } else {
+ $summary = 'HR login will fail or return empty token/modules.';
+ $verdict = 'WILL_FAIL';
+ }
+
+ // Deduplicate issues by code+message
+ $unique = [];
+ $deduped = [];
+ foreach ($issues as $issue) {
+ $key = ($issue['code'] ?? '') . '|' . ($issue['message'] ?? '');
+ if (isset($unique[$key])) {
+ continue;
+ }
+ $unique[$key] = true;
+ $deduped[] = $issue;
+ }
+
+ $postContacts = array_map(static function ($c) {
+ return [
+ 'post_hr_id' => (int) $c['post_hr_id'],
+ 'hr_name' => $c['hr_name'] ?? '',
+ 'hr_mail' => $c['hr_mail'] ?? '',
+ 'hr_mobile' => $c['hr_mobile'] ?? '',
+ 'is_active' => (int) ($c['is_active'] ?? 0),
+ 'post_branch_id' => (int) ($c['post_branch_id'] ?? 0),
+ 'branch_name' => $c['branch_name'] ?? '',
+ 'branch_active' => (int) ($c['branch_active'] ?? 0),
+ 'pre_branch_id' => $c['pre_branch_id'] ?? null,
+ 'post_client_id' => (int) ($c['post_client_id'] ?? 0),
+ 'client_name' => $c['client_name'] ?? '',
+ 'client_active' => (int) ($c['client_active'] ?? 0),
+ 'hr_found' => true,
+ ];
+ }, $contacts);
+
+ $firstPost = $postContacts[0] ?? [];
+ $firstPre = $preMaps[0] ?? [];
+ $tokenCards = array_filter($loginCards, static fn ($c) => ! empty($c['has_token']));
+ $preMatched = array_filter($preMaps, static fn ($m) => ! empty($m['PRE_HR_MATCHED']));
+
+ return [
+ 'input' => ['email' => $email, 'mobile' => $mobile],
+ 'overall_status' => $overall,
+ 'verdict' => $verdict,
+ 'verdict_message' => $summary,
+ 'summary' => $summary,
+ 'issues' => $deduped,
+ 'problems' => $deduped,
+ 'post' => [
+ 'contacts' => $postContacts,
+ 'raw_found' => count($postContacts) > 0,
+ 'eligible' => $anyToken,
+ 'filters_summary' => [
+ 'hr_active' => ! empty($firstPost) && (int) ($firstPost['is_active'] ?? 0) === 1,
+ 'branch_active' => ! empty($firstPost) && (int) ($firstPost['branch_active'] ?? 0) === 1,
+ 'client_active' => ! empty($firstPost) && (int) ($firstPost['client_active'] ?? 0) === 1,
+ 'has_access' => count($tokenCards) > 0,
+ 'token_ready' => $anyToken,
+ ],
+ 'primary' => $firstPost,
+ ],
+ 'hr_access_control' => $accessRows,
+ 'pre' => [
+ 'reachable' => $preReachable,
+ 'mappings' => $preMaps,
+ 'raw_found' => count($preMatched) > 0,
+ 'eligible' => count($preMatched) > 0,
+ 'filters_summary' => [
+ 'db_reachable' => $preReachable === true,
+ 'PRE_BRANCH_LINKED' => ! empty($firstPre['PRE_BRANCH_LINKED']),
+ 'PRE_HR_MATCHED' => ! empty($firstPre['PRE_HR_MATCHED']),
+ 'PRE_IDS_CONSISTENT' => ! empty($firstPre['PRE_IDS_CONSISTENT']),
+ 'PRE_CLIENT_MAPPED' => ! empty($firstPre['PRE_CLIENT_MAPPED']),
+ ],
+ 'primary' => $firstPre,
+ 'error' => $preReachable === false ? 'preDB connection failed' : null,
+ ],
+ 'login_simulation' => [
+ 'would_authenticate' => $wouldAuth,
+ 'cards' => $loginCards,
+ ],
+ 'merge' => [
+ 'post_eligible' => $anyToken,
+ 'pre_eligible' => count($preMatched) > 0,
+ 'post_contacts' => count($postContacts),
+ 'pre_matched' => count($preMatched),
+ 'token_cards' => count($tokenCards),
+ 'access_rows' => count(array_filter($accessRows, static fn ($a) => ! empty($a['id']))),
+ 'would_return_both' => $anyToken && count($preMatched) > 0,
+ 'winner' => ($anyToken && count($preMatched) > 0) ? 'both' : ($anyToken ? 'post' : (count($preMatched) > 0 ? 'pre' : 'none')),
+ 'reason' => $summary,
+ ],
+ ];
+ }
+
+ private function fetchPostContacts($db, string $email, string $mobile): array
+ {
+ $builder = $db->table('level_contacts lc')
+ ->select("
+ lc.id AS post_hr_id,
+ lc.name AS hr_name,
+ lc.email AS hr_mail,
+ lc.mobile AS hr_mobile,
+ lc.is_active,
+ cb.id AS post_branch_id,
+ cb.branch_name,
+ cb.is_active AS branch_active,
+ cb.pre_branch_id,
+ c.id AS post_client_id,
+ c.client_name,
+ c.is_active AS client_active
+ ")
+ ->join('client_branch cb', 'cb.id = lc.ref_id', 'left')
+ ->join('clients c', 'c.id = cb.client_id', 'left')
+ ->where('lc.contact_type', 'client')
+ ->where('lc.is_active', 1);
+
+ if ($email !== '' && $mobile !== '') {
+ $builder->groupStart()
+ ->where('lc.email', $email)
+ ->orWhere('lc.mobile', $mobile)
+ ->groupEnd();
+ } elseif ($email !== '') {
+ $builder->where('lc.email', $email);
+ } else {
+ $builder->where('lc.mobile', $mobile);
+ }
+
+ return $builder->get()->getResultArray();
+ }
+
+ private function fetchAccessRow($db, int $postHrId, int $postBranchId, int $postClientId): ?array
+ {
+ $row = $db->table('hr_access_control')
+ ->where('post_hr_id', $postHrId)
+ ->where('post_branch_id', $postBranchId)
+ ->where('post_client_id', $postClientId)
+ ->where('is_active', 1)
+ ->get()
+ ->getRowArray();
+
+ return $row ?: null;
+ }
+
+ private function hasAnyModule(array $modules): bool
+ {
+ if (isset($modules['pre']) || isset($modules['post'])) {
+ $pre = is_array($modules['pre'] ?? null) ? $modules['pre'] : [];
+ $post = is_array($modules['post'] ?? null) ? $modules['post'] : [];
+
+ return count($pre) > 0 || count($post) > 0;
+ }
+
+ return count($modules) > 0;
+ }
+
+ private function buildPreMapping($preDb, array $contact, ?array $access): array
+ {
+ $result = [
+ 'post_hr_id' => (int) $contact['post_hr_id'],
+ 'pre_hr_id' => null,
+ 'pre_client_id' => null,
+ 'pre_branch_id' => $contact['pre_branch_id'] ?? null,
+ 'pre_client_name' => null,
+ 'pre_branch_name' => null,
+ 'hr_name' => null,
+ 'hr_mail' => null,
+ 'hr_mobile' => null,
+ 'PRE_BRANCH_LINKED' => false,
+ 'PRE_HR_MATCHED' => false,
+ 'PRE_IDS_CONSISTENT' => false,
+ 'PRE_CLIENT_MAPPED' => false,
+ 'access_pre_hr_id' => isset($access['pre_hr_id']) ? (int) $access['pre_hr_id'] : null,
+ 'access_pre_client_id' => isset($access['pre_client_id']) ? (int) $access['pre_client_id'] : null,
+ 'access_pre_branch_id' => isset($access['pre_branch_id']) ? (int) $access['pre_branch_id'] : null,
+ ];
+
+ if ($preDb === null) {
+ return $result;
+ }
+
+ $preBranchId = $contact['pre_branch_id'] ?? null;
+ if (empty($preBranchId)) {
+ return $result;
+ }
+
+ $preBranch = $preDb->table('client_branch cb')
+ ->select('cb.id AS pre_branch_id, cb.client_id AS pre_client_id, cb.branch_name, c.client_name')
+ ->join('clients c', 'c.id = cb.client_id', 'left')
+ ->where('cb.id', $preBranchId)
+ ->where('cb.is_active', 1)
+ ->where('c.is_active', 1)
+ ->get()
+ ->getRowArray();
+
+ if (empty($preBranch)) {
+ return $result;
+ }
+
+ $result['PRE_BRANCH_LINKED'] = true;
+ $result['pre_branch_id'] = (int) $preBranch['pre_branch_id'];
+ $result['pre_client_id'] = (int) $preBranch['pre_client_id'];
+ $result['pre_branch_name'] = $preBranch['branch_name'] ?? null;
+ $result['pre_client_name'] = $preBranch['client_name'] ?? null;
+ $result['PRE_CLIENT_MAPPED'] = true;
+
+ $email = trim((string) ($contact['hr_mail'] ?? ''));
+ $mobile = trim((string) ($contact['hr_mobile'] ?? ''));
+
+ $preHrBuilder = $preDb->table('level_contacts lc')
+ ->select('lc.id AS pre_hr_id, lc.name, lc.email, lc.mobile')
+ ->join('client_branch cb', 'cb.id = lc.ref_id')
+ ->where('lc.contact_type', 'client')
+ ->where('lc.is_active', 1)
+ ->where('cb.is_active', 1)
+ ->where('cb.client_id', $preBranch['pre_client_id']);
+
+ if ($email !== '' && $mobile !== '') {
+ $preHrBuilder->where('lc.email', $email)->where('lc.mobile', $mobile);
+ } elseif ($email !== '') {
+ $preHrBuilder->where('lc.email', $email);
+ } elseif ($mobile !== '') {
+ $preHrBuilder->where('lc.mobile', $mobile);
+ }
+
+ $preHr = $preHrBuilder->get()->getRowArray();
+ if (!empty($preHr)) {
+ $result['PRE_HR_MATCHED'] = true;
+ $result['pre_hr_id'] = (int) $preHr['pre_hr_id'];
+ $result['hr_name'] = $preHr['name'] ?? null;
+ $result['hr_mail'] = $preHr['email'] ?? null;
+ $result['hr_mobile'] = $preHr['mobile'] ?? null;
+ }
+
+ if (!empty($access) && $result['PRE_HR_MATCHED']) {
+ $idsMatch =
+ (int) ($access['pre_hr_id'] ?? 0) === (int) $result['pre_hr_id']
+ && (int) ($access['pre_client_id'] ?? 0) === (int) $result['pre_client_id']
+ && (int) ($access['pre_branch_id'] ?? 0) === (int) $result['pre_branch_id'];
+ $result['PRE_IDS_CONSISTENT'] = $idsMatch;
+ } elseif (empty($access)) {
+ $result['PRE_IDS_CONSISTENT'] = false;
+ }
+
+ return $result;
+ }
+
+ private function issue(string $code, string $severity, string $message, string $fix = ''): array
+ {
+ return [
+ 'code' => $code,
+ 'severity' => $severity,
+ 'message' => $message,
+ 'fix' => $fix,
+ ];
+ }
+}
diff --git a/app/Views/hr_login_diagnose.php b/app/Views/hr_login_diagnose.php
new file mode 100644
index 00000000..7df3c90a
--- /dev/null
+++ b/app/Views/hr_login_diagnose.php
@@ -0,0 +1,590 @@
+
+
+
+
+
+= esc($title ?? 'HR Login Diagnostic') ?>
+
+
+
+
+
+
+
+
HR login diagnostic
+
PRE (enrollment) + POST (live) · hr_access_control · read-only · does not send OTP
+
+
+
+
+
+ 'status-ok',
+ 'OK_WITH_WARNINGS' => 'status-warn',
+ 'PARTIAL' => 'status-partial',
+ default => 'status-fail',
+ };
+ $pre = $result['pre'] ?? [];
+ $post = $result['post'] ?? [];
+ $merge = $result['merge'] ?? [];
+ $problems = $result['problems'] ?? ($result['issues'] ?? []);
+ $accessRows = $result['hr_access_control'] ?? [];
+ $loginSim = $result['login_simulation'] ?? [];
+ $postPrimary = $post['primary'] ?? [];
+ $prePrimary = $pre['primary'] ?? [];
+ $postFs = $post['filters_summary'] ?? [];
+ $preFs = $pre['filters_summary'] ?? [];
+ $mark = static fn (bool $ok) => $ok ? '
✓' : '
✗';
+?>
+
+
+
+ = esc($v) ?>
+ = esc($result['verdict_message'] ?? $result['summary'] ?? '') ?>
+
+
+
+
+
Problems (failed checks only)
+
+
+ | Severity |
+ Code |
+ Message |
+ Fix |
+
+
+ | No problems detected. |
+
+
+
+
+ | = esc(strtoupper($sev)) ?> |
+ = esc($p['code'] ?? '') ?> |
+ = esc($p['message'] ?? '') ?> |
+ = esc($p['fix'] ?? '') ?> |
+
+
+
+
+
+
+
+
PRE (enrollment)
+
+
= esc($pre['error']) ?>
+
+
+
+ | Raw found |
+ = ! empty($pre['raw_found']) ? $mark(true) . ' Yes' : $mark(false) . ' No' ?> |
+
+
+ | Eligible (matched) |
+ = ! empty($pre['eligible']) ? $mark(true) . ' Yes' : $mark(false) . ' No' ?> |
+
+ | preDB reachable | = isset($preFs['db_reachable']) ? $mark((bool) $preFs['db_reachable']) : '—' ?> |
+ | PRE_BRANCH_LINKED | = isset($preFs['PRE_BRANCH_LINKED']) ? $mark((bool) $preFs['PRE_BRANCH_LINKED']) : '—' ?> |
+ | PRE_HR_MATCHED | = isset($preFs['PRE_HR_MATCHED']) ? $mark((bool) $preFs['PRE_HR_MATCHED']) : '—' ?> |
+ | PRE_IDS_CONSISTENT | = isset($preFs['PRE_IDS_CONSISTENT']) ? $mark((bool) $preFs['PRE_IDS_CONSISTENT']) : '—' ?> |
+ | PRE_CLIENT_MAPPED | = isset($preFs['PRE_CLIENT_MAPPED']) ? $mark((bool) $preFs['PRE_CLIENT_MAPPED']) : '—' ?> |
+ | pre_hr_id | = esc((string) ($prePrimary['pre_hr_id'] ?? '—')) ?> |
+ | name | = esc((string) ($prePrimary['hr_name'] ?? '—')) ?> |
+ | email | = esc((string) ($prePrimary['hr_mail'] ?? '—')) ?> |
+ | mobile | = esc((string) ($prePrimary['hr_mobile'] ?? '—')) ?> |
+ | client | = esc((string) (($prePrimary['pre_client_name'] ?? '') . ' #' . ($prePrimary['pre_client_id'] ?? ''))) ?> |
+ | branch | = esc((string) (($prePrimary['pre_branch_name'] ?? '') . ' #' . ($prePrimary['pre_branch_id'] ?? ''))) ?> |
+ | pre_client_id | = esc((string) ($prePrimary['pre_client_id'] ?? '—')) ?> |
+ | pre_branch_id | = esc((string) ($prePrimary['pre_branch_id'] ?? '—')) ?> |
+
+
+
+
+
POST (live)
+
+
+ | Raw found |
+
+ = ! empty($post['raw_found'])
+ ? $mark(true) . ' Yes (' . count($post['contacts'] ?? []) . ')'
+ : $mark(false) . ' No' ?>
+ |
+
+
+ | Eligible for login |
+ = ! empty($post['eligible']) ? $mark(true) . ' Yes' : $mark(false) . ' No' ?> |
+
+ | hr active | = isset($postFs['hr_active']) ? $mark((bool) $postFs['hr_active']) : '—' ?> |
+ | branch active | = isset($postFs['branch_active']) ? $mark((bool) $postFs['branch_active']) : '—' ?> |
+ | client active | = isset($postFs['client_active']) ? $mark((bool) $postFs['client_active']) : '—' ?> |
+ | has access row | = isset($postFs['has_access']) ? $mark((bool) $postFs['has_access']) : '—' ?> |
+ | token ready | = isset($postFs['token_ready']) ? $mark((bool) $postFs['token_ready']) : '—' ?> |
+ | post_hr_id | = esc((string) ($postPrimary['post_hr_id'] ?? '—')) ?> |
+ | name | = esc((string) ($postPrimary['hr_name'] ?? '—')) ?> |
+ | email | = esc((string) ($postPrimary['hr_mail'] ?? '—')) ?> |
+ | mobile | = esc((string) ($postPrimary['hr_mobile'] ?? '—')) ?> |
+ | client | = esc((string) (($postPrimary['client_name'] ?? '') . ' #' . ($postPrimary['post_client_id'] ?? ''))) ?> |
+ | branch | = esc((string) (($postPrimary['branch_name'] ?? '') . ' #' . ($postPrimary['post_branch_id'] ?? ''))) ?> |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | POST eligible | = ! empty($merge['post_eligible']) ? 'Yes' : 'No' ?> |
+ | PRE eligible | = ! empty($merge['pre_eligible']) ? 'Yes' : 'No' ?> |
+ | POST contacts | = esc((string) ($merge['post_contacts'] ?? 0)) ?> |
+ | PRE matched | = esc((string) ($merge['pre_matched'] ?? 0)) ?> |
+ | Access rows with id | = esc((string) ($merge['access_rows'] ?? 0)) ?> |
+ | Token-ready cards | = esc((string) ($merge['token_cards'] ?? 0)) ?> |
+ | Would return both | = ! empty($merge['would_return_both']) ? 'Yes' : 'No' ?> |
+ | Winner | = esc((string) ($merge['winner'] ?? '—')) ?> |
+ | Reason | = esc((string) ($merge['reason'] ?? '')) ?> |
+
+
+
+
+
+
hr_access_control (= count($accessRows) ?>)
+
+
No access rows evaluated.
+
+
+
+
+
+ | Access ID |
+ post_hr_id |
+ Branch / Client |
+ Modules |
+ Token ready |
+ Note |
+
+
+
+
+
+ | = esc((string) ($a['id'] ?? '—')) ?> |
+ = esc((string) ($a['post_hr_id'] ?? '')) ?> |
+ b#= esc((string) ($a['post_branch_id'] ?? '')) ?> / c#= esc((string) ($a['post_client_id'] ?? '')) ?> |
+ = esc(json_encode($a['allowed_modules'] ?? [])) ?> |
+ = ! empty($a['token_ready']) ? 'Yes' : 'No' ?> |
+ = esc((string) ($a['note'] ?? $a['failure_reason'] ?? '')) ?> |
+
+
+
+
+
+
+
+
+
+
+
+
Login simulation (mirrors getVerifiedHrData lookup)
+
+
+ | Would authenticate (OTP identity) |
+ = ! empty($loginSim['would_authenticate']) ? 'Yes' : 'No' ?> |
+
+
+
+
+
No login cards.
+
+
+
+
+
+ | Client |
+ Branch |
+ Has token |
+ Failure |
+
+
+
+
+
+ | = esc((string) ($c['client'] ?? '')) ?> |
+ = esc((string) ($c['branch'] ?? '')) ?> |
+ = ! empty($c['has_token']) ? 'Yes' : 'No' ?> |
+ = esc((string) ($c['failure_reason'] ?? '—')) ?> |
+
+
+
+
+
+
+
+
+
+
+
+
Pre mapping rows
+
+
+
No pre mappings.
+
+
+
+
+
+ | post_hr_id |
+ pre_hr_id |
+ Name |
+ Email |
+ Mobile |
+ Client |
+ Branch |
+ Flags |
+
+
+
+
+
+ | = esc((string) ($m['post_hr_id'] ?? '')) ?> |
+ = esc((string) ($m['pre_hr_id'] ?? '—')) ?> |
+ = esc((string) ($m['hr_name'] ?? '—')) ?> |
+ = esc((string) ($m['hr_mail'] ?? '—')) ?> |
+ = esc((string) ($m['hr_mobile'] ?? '—')) ?> |
+ = esc((string) (($m['pre_client_name'] ?? '') . ' #' . ($m['pre_client_id'] ?? ''))) ?> |
+ = esc((string) (($m['pre_branch_name'] ?? '') . ' #' . ($m['pre_branch_id'] ?? ''))) ?> |
+
+ = ! empty($m['PRE_BRANCH_LINKED']) ? $mark(true) : $mark(false) ?> branch
+ = ! empty($m['PRE_HR_MATCHED']) ? $mark(true) : $mark(false) ?> hr
+ = ! empty($m['PRE_IDS_CONSISTENT']) ? $mark(true) : $mark(false) ?> ids
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
= esc(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)) ?>
+
+
+
+
+
+
+
+ WILL_FAIL
+ No diagnostic result returned.
+
+
+
+
+
+
+