637 lines
29 KiB
PHP
637 lines
29 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use CodeIgniter\API\ResponseTrait;
|
|
|
|
/**
|
|
* Standalone HR login diagnostic tool.
|
|
* Does not modify existing auth / HR access controllers.
|
|
*/
|
|
class HrLoginDiagnoseController extends AdminController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
public function index()
|
|
{
|
|
$email = trim((string) $this->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).',
|
|
[
|
|
'Option A: Verify email/mobile spelling and that level_contacts.contact_type = client and is_active = 1.',
|
|
'Option B: Create/activate the HR contact under the correct client_branch (ref_id = branch id).',
|
|
'Option C: If HR exists only in pre DB, create the matching post level_contacts row first.',
|
|
]
|
|
);
|
|
|
|
return [
|
|
'input' => ['email' => $email, 'mobile' => $mobile],
|
|
'overall_status' => 'FAIL',
|
|
'summary' => 'HR not found in post DB.',
|
|
'issues' => $issues,
|
|
'problems' => $issues,
|
|
'post' => ['contacts' => []],
|
|
'hr_access_control'=> [],
|
|
'pre' => ['reachable' => null, 'mappings' => []],
|
|
'login_simulation' => [
|
|
'would_authenticate' => false,
|
|
'cards' => [],
|
|
],
|
|
];
|
|
}
|
|
|
|
$accessRows = [];
|
|
$preMaps = [];
|
|
$loginCards = [];
|
|
$pairRows = [];
|
|
$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(),
|
|
[
|
|
'Option A: Check preDB hostname/database/username/password in app/Config/Database.php or .env.',
|
|
'Option B: Confirm pre DB server is reachable from this host (network/firewall).',
|
|
'Option C: Re-run diagnose after connectivity is restored to validate PRE mapping.',
|
|
]
|
|
);
|
|
}
|
|
|
|
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']}, {$contact['branch_name']}).",
|
|
[
|
|
"Option A: UPDATE client_branch SET is_active=1 WHERE id={$contact['post_branch_id']};",
|
|
'Option B: In Client Onboarding, activate this branch and re-check login.',
|
|
'Note: Login join requires client_branch.is_active=1.',
|
|
]
|
|
);
|
|
}
|
|
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']}, {$contact['client_name']}).",
|
|
[
|
|
"Option A: UPDATE clients SET is_active=1 WHERE id={$contact['post_client_id']};",
|
|
'Option B: Activate the client in admin and re-run diagnose.',
|
|
'Note: Login join requires clients.is_active=1.',
|
|
]
|
|
);
|
|
}
|
|
|
|
$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']} ({$contact['client_name']} / {$contact['branch_name']}).",
|
|
[
|
|
'Option A: Open Client Onboarding → HR Access for this client, select at least one module for this HR/branch, then Save.',
|
|
"Option B: Insert hr_access_control with post_hr_id={$contact['post_hr_id']}, post_branch_id={$contact['post_branch_id']}, post_client_id={$contact['post_client_id']} and allowed_modules JSON.",
|
|
'Note: Without this row, getVerifiedHrData returns empty token for this card.',
|
|
]
|
|
);
|
|
} 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 for post_hr_id={$contact['post_hr_id']}.",
|
|
[
|
|
'Option A: Open HR Access Control, tick PRE/POST modules for this HR, Save.',
|
|
"Option B: UPDATE hr_access_control SET allowed_modules='{\"pre\":[1],\"post\":[2,3,4]}' WHERE id={$access['id']}; (adjust module ids as needed).",
|
|
'Note: Empty modules → token is treated as not ready.',
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
$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'])) {
|
|
$postBranchId = (int) ($contact['post_branch_id'] ?? 0);
|
|
$issues[] = $this->issue(
|
|
'PRE_BRANCH_NOT_LINKED',
|
|
'medium',
|
|
"Post branch #{$postBranchId} ({$contact['branch_name']}) has no valid pre_branch_id link (current pre_branch_id=" . ($contact['pre_branch_id'] ?? 'null') . ").",
|
|
[
|
|
"Option A: Set client_branch.pre_branch_id on post branch #{$postBranchId} to the matching pre client_branch.id.",
|
|
'Option B: Use Testing/mapping tools to sync post↔pre branch ids, then re-run diagnose.',
|
|
'Option C: Confirm the pre branch exists and is_active=1 before linking.',
|
|
'Note: Without this link, PRE HR/client mapping cannot be resolved.',
|
|
]
|
|
);
|
|
} 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']} "
|
|
. "(post_hr_id={$contact['post_hr_id']}, email={$contact['hr_mail']}, mobile={$contact['hr_mobile']}).",
|
|
[
|
|
'Option A: Align email AND mobile between pre and post level_contacts for this HR.',
|
|
"Option B: Create/activate pre HR contact under pre client #{$preMap['pre_client_id']} with the same email/mobile.",
|
|
'Option C: Check pre contact_type=client and is_active=1 on the matching branch.',
|
|
]
|
|
);
|
|
} elseif (empty($preMap['PRE_IDS_CONSISTENT']) && !empty($access)) {
|
|
$storedHr = (int) ($access['pre_hr_id'] ?? 0);
|
|
$storedClient = (int) ($access['pre_client_id'] ?? 0);
|
|
$storedBranch = (int) ($access['pre_branch_id'] ?? 0);
|
|
$liveHr = (int) ($preMap['pre_hr_id'] ?? 0);
|
|
$liveClient = (int) ($preMap['pre_client_id'] ?? 0);
|
|
$liveBranch = (int) ($preMap['pre_branch_id'] ?? 0);
|
|
$accessId = (int) ($access['id'] ?? 0);
|
|
|
|
$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']} "
|
|
. "(client #{$contact['post_client_id']}, branch #{$contact['post_branch_id']}). "
|
|
. "Stored (hr_access_control id={$accessId}): pre_hr_id={$storedHr}, pre_client_id={$storedClient}, pre_branch_id={$storedBranch}. "
|
|
. "Live pre: pre_hr_id={$liveHr}, pre_client_id={$liveClient}, pre_branch_id={$liveBranch}.",
|
|
[
|
|
'Option A: Open Client Onboarding → HR Access for this client, select modules for this HR, Save (form must send live pre ids).',
|
|
"Option B (recommended): UPDATE hr_access_control SET pre_hr_id={$liveHr}, pre_client_id={$liveClient}, pre_branch_id={$liveBranch} WHERE id={$accessId};",
|
|
'Option C: If UI re-save keeps old ids, auto-refresh in saveHrAccessData is commented out — enable it or use Option B.',
|
|
'Note: POST login can still work; this mainly affects PRE modules/JWT pre mapping.',
|
|
]
|
|
);
|
|
}
|
|
}
|
|
|
|
$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'))),
|
|
];
|
|
|
|
$pairRows[] = [
|
|
'post' => [
|
|
'post_hr_id' => (int) $contact['post_hr_id'],
|
|
'hr_name' => $contact['hr_name'] ?? '',
|
|
'hr_mail' => $contact['hr_mail'] ?? '',
|
|
'hr_mobile' => $contact['hr_mobile'] ?? '',
|
|
'is_active' => (int) ($contact['is_active'] ?? 0),
|
|
'post_branch_id' => (int) ($contact['post_branch_id'] ?? 0),
|
|
'branch_name' => $contact['branch_name'] ?? '',
|
|
'branch_active' => (int) ($contact['branch_active'] ?? 0),
|
|
'pre_branch_id' => $contact['pre_branch_id'] ?? null,
|
|
'post_client_id' => (int) ($contact['post_client_id'] ?? 0),
|
|
'client_name' => $contact['client_name'] ?? '',
|
|
'client_active' => (int) ($contact['client_active'] ?? 0),
|
|
'has_access' => ! empty($access),
|
|
'token_ready' => $tokenReady,
|
|
'hr_active' => (int) ($contact['is_active'] ?? 0) === 1,
|
|
],
|
|
'pre' => $preMap,
|
|
'access_id' => isset($access['id']) ? (int) $access['id'] : null,
|
|
'has_token' => $canLoginCard,
|
|
];
|
|
|
|
$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']));
|
|
|
|
// Group PRE/POST pairs by post client for client-wise display.
|
|
$byClientMap = [];
|
|
foreach ($pairRows as $pair) {
|
|
$cid = (int) ($pair['post']['post_client_id'] ?? 0);
|
|
$key = (string) $cid;
|
|
if (! isset($byClientMap[$key])) {
|
|
$byClientMap[$key] = [
|
|
'post_client_id' => $cid,
|
|
'client_name' => $pair['post']['client_name'] ?? '',
|
|
'client_active' => (int) ($pair['post']['client_active'] ?? 0),
|
|
'rows' => [],
|
|
];
|
|
}
|
|
$byClientMap[$key]['rows'][] = $pair;
|
|
}
|
|
$byClient = array_values($byClientMap);
|
|
usort($byClient, static function ($a, $b) {
|
|
return strcasecmp((string) ($a['client_name'] ?? ''), (string) ($b['client_name'] ?? ''));
|
|
});
|
|
|
|
return [
|
|
'input' => ['email' => $email, 'mobile' => $mobile],
|
|
'overall_status' => $overall,
|
|
'verdict' => $verdict,
|
|
'verdict_message' => $summary,
|
|
'summary' => $summary,
|
|
'issues' => $deduped,
|
|
'problems' => $deduped,
|
|
'by_client' => $byClient,
|
|
'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']))),
|
|
'client_count' => count($byClient),
|
|
'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;
|
|
}
|
|
|
|
/**
|
|
* @param string|array<int, string> $fix
|
|
* @return array{code:string,severity:string,message:string,fix:string,fixes:array<int,string>}
|
|
*/
|
|
private function issue(string $code, string $severity, string $message, $fix = ''): array
|
|
{
|
|
if (is_array($fix)) {
|
|
$fixes = array_values(array_filter(array_map('strval', $fix)));
|
|
} else {
|
|
$fixes = preg_split("/\r\n|\n|\r/", (string) $fix) ?: [];
|
|
$fixes = array_values(array_filter(array_map('trim', $fixes)));
|
|
}
|
|
|
|
return [
|
|
'code' => $code,
|
|
'severity' => $severity,
|
|
'message' => $message,
|
|
'fix' => implode("\n", $fixes),
|
|
'fixes' => $fixes,
|
|
];
|
|
}
|
|
}
|