diff --git a/app/Commands/TestIciciStatusUpdate.php b/app/Commands/TestIciciStatusUpdate.php new file mode 100644 index 00000000..8221514f --- /dev/null +++ b/app/Commands/TestIciciStatusUpdate.php @@ -0,0 +1,413 @@ + 'Optional claim_dump_files.id to process', + ]; + protected $options = [ + '--scenario' => 'Seed a controlled pending dump row for one existing ICICI ticket and test status update', + '--apply' => 'Actually run runTicketMasterInsert (default is map-only dry-run)', + '--new-status' => 'Dump status string to use in scenario (default: REJECTED)', + ]; + + public function run(array $params) + { + helper('utility_helper'); + + $db = db_connect(); + $iciciTpaId = (int) env('ICICI_PRIMARY_KEY_CONSTANT'); + $fileId = $this->resolveFileId($params); + $apply = $this->hasFlag('apply'); + $scenario = $this->hasFlag('scenario'); + $newStatus = $this->resolveOptionValue('new-status', 'REJECTED'); + + CLI::write("ICICI_PRIMARY_KEY_CONSTANT = {$iciciTpaId}", 'yellow'); + CLI::write('Parsed args: file_id=' . $fileId . ', apply=' . ($apply ? 'yes' : 'no') . ', scenario=' . ($scenario ? 'yes' : 'no') . ', new_status=' . $newStatus, 'yellow'); + + if ($scenario) { + $fileId = $this->seedScenario($db, $iciciTpaId, $newStatus); + if ($fileId <= 0) { + return; + } + } + + if ($fileId <= 0) { + $recent = $db->table('claim_dump_files') + ->select('id, tpa_id, client_id, client_policy_id, file_name, status, created_at') + ->where('tpa_id', $iciciTpaId) + ->orderBy('id', 'DESC') + ->limit(10) + ->get() + ->getResultArray(); + + CLI::write('No --file_id provided. Recent ICICI claim_dump_files:', 'cyan'); + if (empty($recent)) { + CLI::error('No ICICI claim dump files found.'); + $this->printMatchPreview($db, $iciciTpaId); + return; + } + + foreach ($recent as $row) { + CLI::write(json_encode($row)); + } + + CLI::newLine(); + CLI::write('Controlled scenario test:', 'green'); + CLI::write(' php spark tpa:test-icici-status --scenario'); + CLI::write(' php spark tpa:test-icici-status --scenario --apply'); + CLI::write('Existing file (use positional file_id):'); + CLI::write(' php spark tpa:test-icici-status 68'); + CLI::write(' php spark tpa:test-icici-status 68 --apply'); + $this->printMatchPreview($db, $iciciTpaId); + return; + } + + $file = $db->table('claim_dump_files')->where('id', $fileId)->get()->getRowArray(); + if (empty($file)) { + CLI::error("claim_dump_files id={$fileId} not found"); + return; + } + + CLI::write('File: ' . json_encode([ + 'id' => $file['id'], + 'tpa_id' => $file['tpa_id'], + 'status' => $file['status'], + 'file_name' => $file['file_name'], + 'client_policy_id' => $file['client_policy_id'], + ]), 'cyan'); + + if ((int) $file['tpa_id'] !== $iciciTpaId) { + CLI::error("File tpa_id={$file['tpa_id']} is not ICICI ({$iciciTpaId}). Aborting."); + return; + } + + $pendingDump = $db->table('claims_dump_icici') + ->where('file_id', $fileId) + ->where('is_active', 1) + ->where('ticket_id IS NULL', null, false) + ->where('master_reject_reason IS NULL', null, false) + ->countAllResults(); + + CLI::write("Pending dump rows for mapClaimMasterData: {$pendingDump}", 'yellow'); + + $service = new IciciClaimImportService(); + + if ($apply) { + CLI::write('APPLY mode: running runTicketMasterInsert...', 'light_red'); + + $pendingBefore = $db->table('claims_dump_icici') + ->select('id, ticket_id, master_reject_reason, updated_status, employee_member_id, uhid, claimed_amount, doa') + ->where('file_id', $fileId) + ->where('is_active', 1) + ->get() + ->getResultArray(); + CLI::write('Dump rows BEFORE: ' . json_encode($pendingBefore, JSON_PRETTY_PRINT)); + + $result = $service->runTicketMasterInsert(['file_id' => $fileId]); + CLI::write('Result: ' . json_encode($result, JSON_PRETTY_PRINT)); + + $pendingAfter = $db->table('claims_dump_icici') + ->select('id, ticket_id, master_reject_reason, updated_status, employee_member_id, uhid, claimed_amount, doa') + ->where('file_id', $fileId) + ->where('is_active', 1) + ->get() + ->getResultArray(); + CLI::write('Dump rows AFTER: ' . json_encode($pendingAfter, JSON_PRETTY_PRINT)); + + $ticketIds = array_values(array_filter(array_column($pendingAfter, 'ticket_id'))); + if (!empty($ticketIds)) { + $ticketsAfter = $db->table('ticket_master') + ->select('id, claim_status_id, claim_dump_ref_id, emp_code, tpa_no, claim_amount, doa') + ->whereIn('id', $ticketIds) + ->get() + ->getResultArray(); + CLI::write('Linked tickets AFTER: ' . json_encode($ticketsAfter, JSON_PRETTY_PRINT)); + } + + return; + } + + CLI::write('DRY-RUN mode: calling mapClaimMasterData only (no DB writes)...', 'green'); + + $ref = new \ReflectionClass($service); + $method = $ref->getMethod('mapClaimMasterData'); + $method->setAccessible(true); + $mapped = $method->invoke($service, $fileId); + + if (empty($mapped['status'])) { + CLI::error('mapClaimMasterData failed: ' . ($mapped['message'] ?? 'unknown')); + if (!empty($mapped['error_data'])) { + CLI::write(json_encode($mapped['error_data'], JSON_PRETTY_PRINT)); + } + return; + } + + $inserts = $mapped['mapped_array'] ?? []; + $rejects = $mapped['rejected_reason_array'] ?? []; + $updates = $mapped['status_update_array'] ?? []; + + CLI::newLine(); + CLI::write('=== RESULT COUNTS ===', 'yellow'); + CLI::write('New tickets to insert : ' . count($inserts)); + CLI::write('Status updates (existing) : ' . count($updates)); + CLI::write('Rejected / skipped rows : ' . count($rejects)); + + CLI::newLine(); + CLI::write('=== STATUS UPDATE ARRAY ===', 'cyan'); + CLI::write(empty($updates) ? '(none)' : json_encode($updates, JSON_PRETTY_PRINT)); + + CLI::newLine(); + CLI::write('=== REJECT / SKIP REASONS (first 20) ===', 'cyan'); + CLI::write(json_encode(array_slice($rejects, 0, 20), JSON_PRETTY_PRINT)); + + if (!empty($updates)) { + CLI::newLine(); + CLI::write('Ticket fields BEFORE update:', 'yellow'); + $ids = array_column($updates, 'id'); + $before = $db->table('ticket_master') + ->select('id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, file_id') + ->whereIn('id', $ids) + ->get() + ->getResultArray(); + CLI::write(json_encode($before, JSON_PRETTY_PRINT)); + } + + CLI::newLine(); + CLI::write('To apply for real:', 'green'); + CLI::write(" php spark tpa:test-icici-status {$fileId} --apply"); + } + + private function resolveFileId(array $params): int + { + if (!empty($params['file_id'])) { + return (int) $params['file_id']; + } + if (!empty($params[0]) && is_numeric($params[0])) { + return (int) $params[0]; + } + + // Fallback for --file_id=68 / --file_id 68 (CI option parsing can miss these) + $argv = $_SERVER['argv'] ?? []; + foreach ($argv as $i => $arg) { + if (preg_match('/^--file[_-]id=(.+)$/', (string) $arg, $m)) { + return (int) $m[1]; + } + if (in_array($arg, ['--file_id', '--file-id'], true) && isset($argv[$i + 1])) { + return (int) $argv[$i + 1]; + } + } + + $opt = CLI::getOption('file_id') ?? CLI::getOption('file-id'); + return (int) ($opt ?? 0); + } + + private function hasFlag(string $name): bool + { + if (CLI::getOption($name) !== null) { + return true; + } + + $argv = $_SERVER['argv'] ?? []; + return in_array('--' . $name, $argv, true); + } + + private function resolveOptionValue(string $name, string $default): string + { + $opt = CLI::getOption($name); + if (is_string($opt) && $opt !== '') { + return $opt; + } + + $argv = $_SERVER['argv'] ?? []; + foreach ($argv as $i => $arg) { + if (preg_match('/^--' . preg_quote($name, '/') . '=(.+)$/', (string) $arg, $m)) { + return trim($m[1]); + } + if ($arg === '--' . $name && isset($argv[$i + 1]) && strpos((string) $argv[$i + 1], '--') !== 0) { + return (string) $argv[$i + 1]; + } + } + + return $default; + } + + /** + * Create a temporary claim_dump_files + one pending claims_dump_icici row + * matching an existing ticket, with a different dump status. + */ + private function seedScenario($db, int $iciciTpaId, string $newStatus): int + { + CLI::write("Seeding controlled ICICI status-update scenario (new_status={$newStatus})...", 'cyan'); + + $ticket = $db->table('ticket_master') + ->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id') + ->where('tpa_id', $iciciTpaId) + ->where('is_active', 1) + ->where('emp_code IS NOT NULL', null, false) + ->where('tpa_no IS NOT NULL', null, false) + ->where('claim_amount IS NOT NULL', null, false) + ->where('doa IS NOT NULL', null, false) + ->orderBy('id', 'DESC') + ->get() + ->getRowArray(); + + if (empty($ticket)) { + // Fall back to any ticket, then override tpa matching by using that ticket's keys + $ticket = $db->table('ticket_master') + ->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id') + ->where('is_active', 1) + ->where('claim_created_by', 'DUMP_TPA') + ->where('emp_code IS NOT NULL', null, false) + ->where('tpa_no IS NOT NULL', null, false) + ->where('claim_amount IS NOT NULL', null, false) + ->where('doa IS NOT NULL', null, false) + ->orderBy('id', 'DESC') + ->get() + ->getRowArray(); + } + + if (empty($ticket)) { + CLI::error('No suitable existing ticket found to seed a status-update scenario.'); + return 0; + } + + CLI::write('Using existing ticket: ' . json_encode([ + 'id' => $ticket['id'], + 'emp_code' => $ticket['emp_code'], + 'tpa_no' => $ticket['tpa_no'], + 'claim_amount' => $ticket['claim_amount'], + 'doa' => $ticket['doa'], + 'claim_status_id' => $ticket['claim_status_id'], + 'claim_dump_ref_id' => $ticket['claim_dump_ref_id'] ?? null, + 'tpa_id' => $ticket['tpa_id'], + ]), 'yellow'); + + // Clear stale claim_dump_ref_id so the scenario can re-test link backfill. + if (!empty($ticket['claim_dump_ref_id'])) { + $db->table('ticket_master')->where('id', $ticket['id'])->update([ + 'claim_dump_ref_id' => null, + ]); + $ticket['claim_dump_ref_id'] = null; + CLI::write('Cleared stale ticket.claim_dump_ref_id for retest.', 'yellow'); + } + + // Ensure a physical file exists for resolveTpaClaimDumpFile if --apply uses controller later + $uploadDir = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0775, true); + } + $fileName = 'icici_status_test_' . date('Ymd_His') . '.xlsx'; + $filePath = $uploadDir . $fileName; + if (!is_file($filePath)) { + file_put_contents($filePath, 'placeholder'); + } + + $fileInsert = [ + 'tpa_id' => $iciciTpaId, + 'client_id' => $ticket['client_id'], + 'client_policy_id' => $ticket['client_policy_id'], + 'file_name' => $fileName, + 'status' => 'pending', + 'created_by' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'is_active' => 1, + ]; + + // claim_dump_files may have different nullable columns; insert only known-safe ones + $fileCols = array_column($db->query('SHOW COLUMNS FROM claim_dump_files')->getResultArray(), 'Field'); + $fileInsert = array_intersect_key($fileInsert, array_flip($fileCols)); + + $db->table('claim_dump_files')->insert($fileInsert); + $fileId = (int) $db->insertID(); + if ($fileId <= 0) { + CLI::error('Failed to create claim_dump_files row for scenario.'); + return 0; + } + + $dumpInsert = [ + 'client_id' => $ticket['client_id'], + 'client_policy_id' => $ticket['client_policy_id'], + 'file_id' => $fileId, + 'employee_member_id' => $ticket['emp_code'], + 'uhid' => $ticket['tpa_no'], + 'claimed_amount' => $ticket['claim_amount'], + 'doa' => $ticket['doa'], + 'updated_status' => $newStatus, + 'relation' => 'SELF', + 'is_active' => 1, + 'created_by' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'ticket_id' => null, + 'master_reject_reason' => null, + ]; + + $dumpCols = array_column($db->query('SHOW COLUMNS FROM claims_dump_icici')->getResultArray(), 'Field'); + $dumpInsert = array_intersect_key($dumpInsert, array_flip($dumpCols)); + + $db->table('claims_dump_icici')->insert($dumpInsert); + $dumpId = (int) $db->insertID(); + + CLI::write("Seeded file_id={$fileId}, dump_id={$dumpId}", 'green'); + CLI::write("Expected: if ticket claim_status_id ({$ticket['claim_status_id']}) != mapped REJECTED(8)/chosen status, status_update_array should contain ticket {$ticket['id']}", 'green'); + + return $fileId; + } + + private function printMatchPreview($db, int $iciciTpaId): void + { + CLI::newLine(); + CLI::write('Preview: dump rows that match existing tickets (possible status updates)', 'cyan'); + + $sql = " + SELECT + cd.id AS dump_id, + cd.file_id, + cd.employee_member_id, + cd.uhid, + cd.claimed_amount, + cd.doa, + cd.updated_status AS dump_status, + tm.id AS ticket_id, + tm.claim_status_id AS current_status_id + FROM claims_dump_icici cd + INNER JOIN ticket_master tm + ON tm.is_active = 1 + AND tm.emp_code = cd.employee_member_id + AND tm.tpa_no = cd.uhid + AND tm.claim_amount = cd.claimed_amount + AND tm.doa = cd.doa + WHERE cd.is_active = 1 + AND tm.tpa_id = ? + ORDER BY cd.id DESC + LIMIT 10 + "; + + $rows = $db->query($sql, [$iciciTpaId])->getResultArray(); + if (empty($rows)) { + CLI::write('No overlapping dump/ticket pairs found.'); + return; + } + + CLI::write(json_encode($rows, JSON_PRETTY_PRINT)); + } +} diff --git a/app/Commands/TestTpaStatusUpdate.php b/app/Commands/TestTpaStatusUpdate.php new file mode 100644 index 00000000..6dc61edc --- /dev/null +++ b/app/Commands/TestTpaStatusUpdate.php @@ -0,0 +1,524 @@ + 'Optional claim_dump_files.id to process', + ]; + protected $options = [ + '--tpa' => 'TPA key: icici|abhi|mediassist|fhpl|rcare|vidal|all (default: all for --scenario)', + '--scenario' => 'Seed one pending dump row matching an existing ticket', + '--apply' => 'Actually run runTicketMasterInsert (default is map-only dry-run)', + '--new-status' => 'Dump status string for scenario (defaults per TPA)', + ]; + + /** + * Per-TPA dump column mapping used to seed scenario rows. + */ + private function tpaConfigs(): array + { + return [ + 'icici' => [ + 'env' => 'ICICI_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_icici', + 'status_column' => 'updated_status', + 'default_status'=> 'REJECTED', + 'same_status' => 'PAID', + 'dump_to_ticket'=> [ + 'employee_member_id' => 'emp_code', + 'uhid' => 'tpa_no', + 'claimed_amount' => 'claim_amount', + 'doa' => 'doa', + ], + 'extra_dump' => [ + 'relation' => 'SELF', + ], + ], + 'abhi' => [ + 'env' => 'ABHI_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_abhi', + 'status_column' => 'claim_status', + 'default_status'=> 'Rejected', + 'same_status' => 'Settled', + 'dump_to_ticket'=> [ + 'member_code' => 'emp_code', + 'healthcard_id' => 'tpa_no', + 'claimed_amount' => 'claim_amount', + 'doa' => 'doa', + ], + 'extra_dump' => [ + 'relation' => 'SELF', + ], + ], + 'mediassist' => [ + 'env' => 'MEDI_ASSIST_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_medi_assist', + 'status_column' => 'claim_status', + 'default_status'=> 'Rejected', + 'same_status' => 'Settled', + 'dump_to_ticket'=> [ + 'pribenef_employee_code' => 'emp_code', + 'event_id' => 'tpa_no', + 'claim_amount' => 'claim_amount', + 'date_of_admission' => 'doa', + ], + 'extra_dump' => [ + 'benef_relation' => 'SELF', + ], + ], + 'fhpl' => [ + 'env' => 'FHPL_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_fhpl', + 'status_column' => 'current_claim_status', + 'default_status'=> 'Rejected', + 'same_status' => 'Settled', + 'dump_to_ticket'=> [ + 'employee_id' => 'emp_code', + 'uhid_no' => 'tpa_no', + 'claim_amount' => 'claim_amount', + 'admission_date' => 'doa', + ], + 'extra_dump' => [ + 'relationship' => 'SELF', + ], + ], + 'rcare' => [ + 'env' => 'R_CARE_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_reliance', + 'status_column' => 'final_status', + 'default_status'=> 'Rejected', + 'same_status' => 'Settled', + 'dump_to_ticket'=> [ + 'employee_member_id' => 'emp_code', + 'uhid' => 'tpa_no', + 'claimed_amount' => 'claim_amount', + 'doa_opd_treatment_from' => 'doa', + ], + 'extra_dump' => [ + 'relation' => 'SELF', + ], + ], + 'vidal' => [ + 'env' => 'VIDAL_PRIMARY_KEY_CONSTANT', + 'table' => 'claims_dump_vidal', + 'status_column' => 'claim_status', + 'default_status'=> 'Rejected', + 'same_status' => 'Settled', + 'dump_to_ticket'=> [ + 'employee_number' => 'emp_code', + 'primary_policy_holder_card_id' => 'tpa_no', + 'claim_amount' => 'claim_amount', + 'date_of_admission' => 'doa', + ], + 'extra_dump' => [ + 'relation' => 'SELF', + ], + ], + ]; + } + + public function run(array $params) + { + helper('utility_helper'); + + $db = db_connect(); + $apply = $this->hasFlag('apply'); + $scenario = $this->hasFlag('scenario'); + $tpaOpt = strtolower((string) ($this->resolveOptionValue('tpa', '') ?: (CLI::getOption('tpa') ?? ''))); + $fileId = $this->resolveFileId($params); + $newStatusOverride = $this->resolveOptionValue('new-status', ''); + + $configs = $this->tpaConfigs(); + + // Resolve which TPAs to run + if ($tpaOpt === '' || $tpaOpt === 'all') { + $selected = array_keys($configs); + if (!$scenario && $fileId <= 0) { + $this->printUsage($configs); + return; + } + } elseif (isset($configs[$tpaOpt])) { + $selected = [$tpaOpt]; + } else { + CLI::error("Unknown --tpa={$tpaOpt}. Use: " . implode('|', array_keys($configs)) . '|all'); + return; + } + + foreach ($selected as $tpaKey) { + CLI::newLine(); + CLI::write(str_repeat('=', 60), 'yellow'); + CLI::write('TPA: ' . strtoupper($tpaKey), 'yellow'); + CLI::write(str_repeat('=', 60), 'yellow'); + + try { + $this->runForTpa( + $db, + $tpaKey, + $configs[$tpaKey], + $scenario, + $apply, + $fileId, + $newStatusOverride + ); + } catch (\Throwable $e) { + CLI::error("[{$tpaKey}] " . $e->getMessage()); + } + } + } + + private function runForTpa( + $db, + string $tpaKey, + array $cfg, + bool $scenario, + bool $apply, + int $fileId, + string $newStatusOverride + ): void { + $tpaId = (int) env($cfg['env']); + CLI::write("{$cfg['env']} = {$tpaId}", 'cyan'); + CLI::write("table={$cfg['table']}, status_column={$cfg['status_column']}", 'cyan'); + + if (!$this->tableExists($db, $cfg['table'])) { + CLI::error("Table {$cfg['table']} does not exist. Skipping."); + return; + } + + $newStatus = $newStatusOverride !== '' ? $newStatusOverride : $cfg['default_status']; + $currentFileId = $fileId; + + if ($scenario) { + $currentFileId = $this->seedScenario($db, $tpaKey, $cfg, $tpaId, $newStatus); + if ($currentFileId <= 0) { + return; + } + } + + if ($currentFileId <= 0) { + $this->listRecentFiles($db, $tpaId, $tpaKey); + return; + } + + $file = $db->table('claim_dump_files')->where('id', $currentFileId)->get()->getRowArray(); + if (empty($file)) { + CLI::error("claim_dump_files id={$currentFileId} not found"); + return; + } + + CLI::write('File: ' . json_encode([ + 'id' => $file['id'], + 'tpa_id' => $file['tpa_id'], + 'status' => $file['status'], + 'file_name' => $file['file_name'], + 'client_policy_id' => $file['client_policy_id'], + ])); + + if ((int) $file['tpa_id'] !== $tpaId) { + CLI::error("File tpa_id={$file['tpa_id']} does not match {$tpaKey} ({$tpaId}). Skipping."); + return; + } + + $pending = $db->table($cfg['table']) + ->where('file_id', $currentFileId) + ->where('is_active', 1) + ->where('ticket_id IS NULL', null, false) + ->where('master_reject_reason IS NULL', null, false) + ->countAllResults(); + CLI::write("Pending dump rows: {$pending}"); + + $service = TpaClaimsImportFactory::make($tpaId); + + if ($apply) { + CLI::write('APPLY mode...', 'light_red'); + + $beforeDump = $db->table($cfg['table']) + ->select($this->dumpSelectColumns($cfg)) + ->where('file_id', $currentFileId) + ->where('is_active', 1) + ->get()->getResultArray(); + CLI::write('Dump BEFORE: ' . json_encode($beforeDump, JSON_PRETTY_PRINT)); + + $result = $service->runTicketMasterInsert(['file_id' => $currentFileId]); + CLI::write('Result: ' . json_encode($result, JSON_PRETTY_PRINT)); + + $afterDump = $db->table($cfg['table']) + ->select($this->dumpSelectColumns($cfg)) + ->where('file_id', $currentFileId) + ->where('is_active', 1) + ->get()->getResultArray(); + CLI::write('Dump AFTER: ' . json_encode($afterDump, JSON_PRETTY_PRINT)); + + $ticketIds = array_values(array_filter(array_column($afterDump, 'ticket_id'))); + if (!empty($ticketIds)) { + $tickets = $db->table('ticket_master') + ->select('id, claim_status_id, claim_dump_ref_id, emp_code, tpa_no, claim_amount, doa') + ->whereIn('id', $ticketIds) + ->get()->getResultArray(); + CLI::write('Tickets AFTER: ' . json_encode($tickets, JSON_PRETTY_PRINT)); + } + return; + } + + CLI::write('DRY-RUN mode (mapClaimMasterData only)...', 'green'); + $ref = new \ReflectionClass($service); + $method = $ref->getMethod('mapClaimMasterData'); + $method->setAccessible(true); + $mapped = $method->invoke($service, $currentFileId); + + if (empty($mapped['status'])) { + CLI::write('mapClaimMasterData: ' . ($mapped['message'] ?? 'failed')); + if (!empty($mapped['already_processed'])) { + CLI::write('(already processed — dump rows were NOT deleted)', 'green'); + } + return; + } + + $inserts = $mapped['mapped_array'] ?? []; + $rejects = $mapped['rejected_reason_array'] ?? []; + $updates = $mapped['status_update_array'] ?? []; + + CLI::write('inserts=' . count($inserts) . ' updates=' . count($updates) . ' rejects=' . count($rejects)); + CLI::write('status_update_array: ' . (empty($updates) ? '(none)' : json_encode($updates, JSON_PRETTY_PRINT))); + CLI::write('rejected_reason_array: ' . json_encode(array_slice($rejects, 0, 10), JSON_PRETTY_PRINT)); + + if (!empty($updates)) { + $ids = array_column($updates, 'id'); + $before = $db->table('ticket_master') + ->select('id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id') + ->whereIn('id', $ids) + ->get()->getResultArray(); + CLI::write('Tickets BEFORE: ' . json_encode($before, JSON_PRETTY_PRINT)); + } + + CLI::write("To apply: php spark tpa:test-status --tpa={$tpaKey} {$currentFileId} --apply", 'green'); + } + + private function seedScenario($db, string $tpaKey, array $cfg, int $tpaId, string $newStatus): int + { + CLI::write("Seeding scenario for {$tpaKey} (new_status={$newStatus})...", 'cyan'); + + $ticket = $db->table('ticket_master') + ->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id') + ->where('tpa_id', $tpaId) + ->where('is_active', 1) + ->where('emp_code IS NOT NULL', null, false) + ->where('tpa_no IS NOT NULL', null, false) + ->where('claim_amount IS NOT NULL', null, false) + ->where('doa IS NOT NULL', null, false) + ->orderBy('id', 'DESC') + ->get()->getRowArray(); + + if (empty($ticket)) { + // Fall back: any ticket with identity keys (still allows match by doa/emp/amount/tpa_no) + $ticket = $db->table('ticket_master') + ->select('id, client_id, client_policy_id, emp_code, tpa_no, claim_amount, doa, claim_status_id, claim_dump_ref_id, tpa_id') + ->where('is_active', 1) + ->where('emp_code IS NOT NULL', null, false) + ->where('tpa_no IS NOT NULL', null, false) + ->where('claim_amount IS NOT NULL', null, false) + ->where('doa IS NOT NULL', null, false) + ->orderBy('id', 'DESC') + ->get()->getRowArray(); + } + + if (empty($ticket)) { + CLI::error("[{$tpaKey}] No suitable existing ticket found to seed."); + return 0; + } + + CLI::write('Using ticket: ' . json_encode([ + 'id' => $ticket['id'], + 'emp_code' => $ticket['emp_code'], + 'tpa_no' => $ticket['tpa_no'], + 'claim_amount' => $ticket['claim_amount'], + 'doa' => $ticket['doa'], + 'claim_status_id' => $ticket['claim_status_id'], + 'claim_dump_ref_id' => $ticket['claim_dump_ref_id'], + 'tpa_id' => $ticket['tpa_id'], + ])); + + if (!empty($ticket['claim_dump_ref_id'])) { + $db->table('ticket_master')->where('id', $ticket['id'])->update(['claim_dump_ref_id' => null]); + CLI::write('Cleared stale ticket.claim_dump_ref_id for retest.', 'yellow'); + } + + $uploadDir = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'claim_dump_excel' . DIRECTORY_SEPARATOR; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0775, true); + } + $fileName = "{$tpaKey}_status_test_" . date('Ymd_His') . '.xlsx'; + file_put_contents($uploadDir . $fileName, 'placeholder'); + + $fileInsert = [ + 'tpa_id' => $tpaId, + 'client_id' => $ticket['client_id'], + 'client_policy_id' => $ticket['client_policy_id'], + 'file_name' => $fileName, + 'status' => 'pending', + 'created_by' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'is_active' => 1, + ]; + $fileCols = array_column($db->query('SHOW COLUMNS FROM claim_dump_files')->getResultArray(), 'Field'); + $fileInsert = array_intersect_key($fileInsert, array_flip($fileCols)); + $db->table('claim_dump_files')->insert($fileInsert); + $fileId = (int) $db->insertID(); + if ($fileId <= 0) { + CLI::error("[{$tpaKey}] Failed to create claim_dump_files row."); + return 0; + } + + $dumpInsert = [ + 'client_id' => $ticket['client_id'], + 'client_policy_id' => $ticket['client_policy_id'], + 'file_id' => $fileId, + 'is_active' => 1, + 'created_by' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'ticket_id' => null, + 'master_reject_reason' => null, + $cfg['status_column'] => $newStatus, + ]; + + foreach ($cfg['dump_to_ticket'] as $dumpCol => $ticketCol) { + $dumpInsert[$dumpCol] = $ticket[$ticketCol] ?? null; + } + foreach ($cfg['extra_dump'] as $col => $val) { + $dumpInsert[$col] = $val; + } + + $dumpCols = array_column($db->query('SHOW COLUMNS FROM ' . $cfg['table'])->getResultArray(), 'Field'); + $dumpInsert = array_intersect_key($dumpInsert, array_flip($dumpCols)); + + $db->table($cfg['table'])->insert($dumpInsert); + $dumpId = (int) $db->insertID(); + + CLI::write("Seeded file_id={$fileId}, dump_id={$dumpId}", 'green'); + return $fileId; + } + + private function dumpSelectColumns(array $cfg): string + { + $cols = ['id', 'ticket_id', 'master_reject_reason', 'file_id', $cfg['status_column']]; + foreach (array_keys($cfg['dump_to_ticket']) as $col) { + $cols[] = $col; + } + return implode(', ', array_unique($cols)); + } + + private function listRecentFiles($db, int $tpaId, string $tpaKey): void + { + $recent = $db->table('claim_dump_files') + ->select('id, tpa_id, client_id, client_policy_id, file_name, status, created_at') + ->where('tpa_id', $tpaId) + ->orderBy('id', 'DESC') + ->limit(5) + ->get()->getResultArray(); + + CLI::write("Recent {$tpaKey} claim_dump_files:"); + if (empty($recent)) { + CLI::write('(none)'); + } else { + foreach ($recent as $row) { + CLI::write(json_encode($row)); + } + } + CLI::write("Seed: php spark tpa:test-status --tpa={$tpaKey} --scenario", 'green'); + CLI::write("Apply: php spark tpa:test-status --tpa={$tpaKey} --scenario --apply", 'green'); + } + + private function printUsage(array $configs): void + { + CLI::write('Test existing-ticket status/link update for TPA claim dumps.', 'cyan'); + CLI::newLine(); + CLI::write('Dry-run all TPAs (seed scenario, no DB ticket update):'); + CLI::write(' php spark tpa:test-status --tpa=all --scenario'); + CLI::newLine(); + CLI::write('Apply all TPAs (writes to DB):'); + CLI::write(' php spark tpa:test-status --tpa=all --scenario --apply'); + CLI::newLine(); + CLI::write('Single TPA:'); + foreach (array_keys($configs) as $key) { + CLI::write(" php spark tpa:test-status --tpa={$key} --scenario"); + CLI::write(" php spark tpa:test-status --tpa={$key} --scenario --apply"); + } + CLI::newLine(); + CLI::write('Same-status (no status change, only null-link fill):'); + CLI::write(' php spark tpa:test-status --tpa=abhi --scenario --new-status Settled'); + } + + private function tableExists($db, string $table): bool + { + return !empty($db->query("SHOW TABLES LIKE " . $db->escape($table))->getResultArray()); + } + + private function resolveFileId(array $params): int + { + if (!empty($params['file_id'])) { + return (int) $params['file_id']; + } + if (!empty($params[0]) && is_numeric($params[0])) { + return (int) $params[0]; + } + + $argv = $_SERVER['argv'] ?? []; + foreach ($argv as $i => $arg) { + if (preg_match('/^--file[_-]id=(.+)$/', (string) $arg, $m)) { + return (int) $m[1]; + } + if (in_array($arg, ['--file_id', '--file-id'], true) && isset($argv[$i + 1])) { + return (int) $argv[$i + 1]; + } + } + + return (int) (CLI::getOption('file_id') ?? CLI::getOption('file-id') ?? 0); + } + + private function hasFlag(string $name): bool + { + if (CLI::getOption($name) !== null) { + return true; + } + return in_array('--' . $name, $_SERVER['argv'] ?? [], true); + } + + private function resolveOptionValue(string $name, string $default): string + { + $opt = CLI::getOption($name); + if (is_string($opt) && $opt !== '') { + return $opt; + } + + $argv = $_SERVER['argv'] ?? []; + foreach ($argv as $i => $arg) { + if (preg_match('/^--' . preg_quote($name, '/') . '=(.+)$/', (string) $arg, $m)) { + return trim($m[1]); + } + if ($arg === '--' . $name && isset($argv[$i + 1]) && strpos((string) $argv[$i + 1], '--') !== 0) { + return (string) $argv[$i + 1]; + } + } + + return $default; + } +} diff --git a/app/Config/Routes.php b/app/Config/Routes.php index e18e3df7..9989ee54 100755 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -501,6 +501,8 @@ $routes->group("policy_tranction", ["filter" => "authMVC"], function ($routes) { $routes->group("inception", ["filter" => "authMVC"], function ($routes) { $routes->match(['get', 'post'], 'list', 'PolicyTransactionController::viewInception'); + $routes->post('list/datatable', 'PolicyTransactionController::inceptionListDataTable'); + $routes->post('list/clear-cache', 'PolicyTransactionController::clearInceptionListCache'); $routes->post("create", "PolicyTransactionController::createInceptionPolicy"); $routes->get("list/(:any)", "PolicyTransactionController::getInceptionDataForEdit/$1"); $routes->get("remove/(:any)", "PolicyTransactionController::removeCDMaster/$1"); @@ -511,6 +513,8 @@ $routes->group("policy_tranction", ["filter" => "authMVC"], function ($routes) { $routes->group("endorsement", ["filter" => "authMVC"], function ($routes) { $routes->get("list", "PolicyTransactionController::viewEndorsement"); + $routes->post("list/datatable", "PolicyTransactionController::endorsementListDataTable"); + $routes->post("list/clear-cache", "PolicyTransactionController::clearEndorsementListCache"); $routes->post("create", "PolicyTransactionController::createEndorsementPolicy"); $routes->get("list/(:any)", "PolicyTransactionController::getEndorsementDataForEdit/$1"); $routes->get("remove/(:any)", "PolicyTransactionController::removeCDMaster/$1"); @@ -520,6 +524,8 @@ $routes->group("policy_tranction", ["filter" => "authMVC"], function ($routes) { $routes->group("report", ["filter" => "authMVC"], function ($routes) { $routes->match(['get', 'post'],"list", "PolicyTransactionController::reportBDS"); + $routes->post("list/datatable", "PolicyTransactionController::reportBDSDataTable"); + $routes->post("list/clear-cache", "PolicyTransactionController::clearReportBDSCache"); $routes->match(['get', 'post'],"list_new", "PolicyTransactionController::reportBDSNew"); $routes->get("report-varience-list", "PolicyTransactionController::reportVarience"); $routes->get("report-business-list", "PolicyTransactionController::reportBusinessList"); diff --git a/app/Controllers/EmployeeRestController.php b/app/Controllers/EmployeeRestController.php index dd4a261c..dfcf11a6 100755 --- a/app/Controllers/EmployeeRestController.php +++ b/app/Controllers/EmployeeRestController.php @@ -483,6 +483,33 @@ class EmployeeRestController extends AdminController } } + private function getClaimStatusReason(string $claimStatus, array $claimsData): string + { + $statusReasonConfig = [ + 'REJECTED' => ['denial_reason', 'head_rejection_reason'], + 'CANCELLED' => ['cancel_remark'], + 'CANCELED' => ['cancel_remark'], + 'RETURNED' => ['return_remark'], + ]; + + $claimStatusKey = strtoupper($claimStatus); + + if (! isset($statusReasonConfig[$claimStatusKey])) { + return ''; + } + + foreach ($statusReasonConfig[$claimStatusKey] as $field) { + if (! empty($claimsData[$field])) { + $reason = trim((string) $claimsData[$field]); + if ($reason !== '') { + return $reason; + } + } + } + + return ''; + } + public function deleteDependence() { try { @@ -2779,10 +2806,19 @@ class EmployeeRestController extends AdminController $unique_key = $display_name; } - $filtered_history[$unique_key] = [ + $modifiedAt = date('d-m-Y h:i A', strtotime($value['created_at'])); + $reason = $this->getClaimStatusReason($status, $data['claims_data']); + + $statusEntry = [ 'modified_by' => "", - 'modified_at' => date('d-m-Y h:i A', strtotime($value['created_at'])), + 'modified_at' => $modifiedAt, ]; + + if ($reason !== '') { + $statusEntry['reason'] = ' Reason : ' . $reason; + } + + $filtered_history[$unique_key] = $statusEntry; } } } diff --git a/app/Controllers/LeadsController.php b/app/Controllers/LeadsController.php index bd343eee..231ce0e1 100644 --- a/app/Controllers/LeadsController.php +++ b/app/Controllers/LeadsController.php @@ -2147,6 +2147,59 @@ class LeadsController extends BaseController return $result; } + private function saveLeadInstallmentDetails(int $lead_id, $installments): array + { + if (is_string($installments)) { + $installments = json_decode($installments, true) ?? []; + } + + if (! is_array($installments) || empty($installments)) { + return []; + } + + $existingInstallments = $this->leadInstallmentPaymentDetails + ->where('lead_id', $lead_id) + ->where('is_active', 1) + ->orderBy('id', 'ASC') + ->findAll(); + + $savedInstallments = []; + + foreach ($installments as $index => $installment) { + if (! is_array($installment)) { + continue; + } + + $data = [ + 'lead_id' => $lead_id, + 'installment_amount' => $installment['installment_amount'] ?? null, + 'payment_date' => ! empty($installment['payment_date']) + ? change_date_format($installment['payment_date']) + : null, + 'utr_no' => $installment['utr_no'] ?? null, + ]; + + $installmentId = ! empty($installment['id']) ? (int) $installment['id'] : null; + + if (! $installmentId && isset($existingInstallments[$index]['id'])) { + $installmentId = (int) $existingInstallments[$index]['id']; + } + + if ($installmentId) { + $this->leadInstallmentPaymentDetails->update($installmentId, $data); + $data['id'] = $installmentId; + } else { + $data['is_active'] = 1; + $newId = $this->leadInstallmentPaymentDetails->insert($data); + $data['id'] = $newId; + } + + $savedInstallments[] = $data; + } + + return $savedInstallments; + } + public function removeInstallments() { $id = $this->request->getGet('id'); @@ -3811,21 +3864,7 @@ class LeadsController extends BaseController $this->leadsModel->where('id', $lead_id)->set($data)->update(); if (isset($params['installments']) && ! empty($params['installments'])) { - - $installment_data = json_decode($params['installments'], true); - if (! empty($installment_data)) { - foreach ($installment_data as $key => $value) { - // print_r($value);die - $value['payment_date'] = ! empty($value['payment_date']) - ? change_date_format($value['payment_date']) - : null; - if (isset($value['id']) && ! empty($value['id'])) { - $this->leadInstallmentPaymentDetails->where('id', $value['id'])->set($value)->update(); - } else { - $this->leadInstallmentPaymentDetails->insert($value); - } - } - } + $this->saveLeadInstallmentDetails((int) $lead_id, $params['installments']); } } @@ -8491,29 +8530,12 @@ class LeadsController extends BaseController $this->myLogger->logme('error', "Lead updated successfully for ID: $lead_id"); // Save installment details + $savedInstallments = []; if (! empty($params['installments'])) { - $installments = json_decode($params['installments'], true); - $this->myLogger->logme('error', "Installments data received: " . json_encode($installments)); - - if (is_array($installments) && ! empty($installments)) { - foreach ($installments as $installment) { - $installment['payment_date'] = ! empty($installment['payment_date']) - ? change_date_format($installment['payment_date']) - : null; - - $installment['lead_id'] = $lead_id; - - if (! empty($installment['id'])) { - $this->leadInstallmentPaymentDetails->update($installment['id'], $installment); - $this->myLogger->logme('error', "Installment updated: " . json_encode($installment)); - } else { - $this->leadInstallmentPaymentDetails->insert($installment); - $this->myLogger->logme('error', "Installment inserted: " . json_encode($installment)); - } - } - } + $savedInstallments = $this->saveLeadInstallmentDetails((int) $lead_id, $params['installments']); + $this->myLogger->logme('error', 'Installments saved: ' . json_encode($savedInstallments)); } else { - $this->myLogger->logme('error', "No installments provided"); + $this->myLogger->logme('error', 'No installments provided'); } // Update lead file status to pending @@ -8526,12 +8548,13 @@ class LeadsController extends BaseController $this->myLogger->logme('error', "--- savePlacementDataAndValidateMemberDataFile END (SUCCESS) ---"); return $this->respond([ - 'status' => true, - 'code' => 200, - 'message' => 'Placement data saved successfully. File being validated', - 'lead_id' => $lead_id, - 'data' => $data, - 'params' => $params, + 'status' => true, + 'code' => 200, + 'message' => 'Placement data saved successfully. File being validated', + 'lead_id' => $lead_id, + 'installments' => $savedInstallments, + 'data' => $data, + 'params' => $params, ], 200); } catch (\Exception $e) { @@ -8614,31 +8637,17 @@ class LeadsController extends BaseController $this->leadsModel->update($lead_id, $data); + $savedInstallments = []; if (! empty($params['installments'])) { - $installments = json_decode($params['installments'], true); - - if (is_array($installments) && ! empty($installments)) { - foreach ($installments as $installment) { - $installment['payment_date'] = ! empty($installment['payment_date']) - ? change_date_format($installment['payment_date']) - : null; - - $installment['lead_id'] = $lead_id; - - if (! empty($installment['id'])) { - $this->leadInstallmentPaymentDetails->update($installment['id'], $installment); - } else { - $this->leadInstallmentPaymentDetails->insert($installment); - } - } - } + $savedInstallments = $this->saveLeadInstallmentDetails((int) $lead_id, $params['installments']); } return $this->respond([ - 'status' => true, - 'code' => 200, - 'message' => 'Placement data saved successfully', - 'lead_id' => $lead_id, + 'status' => true, + 'code' => 200, + 'message' => 'Placement data saved successfully', + 'lead_id' => $lead_id, + 'installments' => $savedInstallments, ], 200); } catch (\Exception $e) { return $this->respond([ diff --git a/app/Controllers/PolicyTransactionController.php b/app/Controllers/PolicyTransactionController.php index 8c2436d7..818c3d1f 100644 --- a/app/Controllers/PolicyTransactionController.php +++ b/app/Controllers/PolicyTransactionController.php @@ -602,59 +602,9 @@ class PolicyTransactionController extends BaseController 'policy_end_date' => 'Policy End Date', ]; - // Filter data - $start_date = $this->request->getGet('start_date'); - $end_date = $this->request->getGet('end_date'); - $client_id = $this->request->getGet('client_id'); - $insurer_id = $this->request->getGet('insurer_id'); - $policy_type_id = $this->request->getGet('policy_type_id'); - $date_type = $this->request->getGet('date_type'); - $issuer = $this->request->getGet('issuer'); - $status = $this->request->getGet('status'); - - // Handle null or empty values - $start_date = empty($start_date) ? 0 : $start_date; - $end_date = empty($end_date) ? 0 : $end_date; - $client_id = empty($client_id) ? 0 : $client_id; - $insurer_id = empty($insurer_id) ? 0 : $insurer_id; - $policy_type_id = empty($policy_type_id) ? 0 : $policy_type_id; - $date_type = empty($date_type) ? 0 : $date_type; - $issuer = empty($issuer) ? 0 : $issuer; - $status = empty($status) ? 0 : $status; // Corrected from `$issuer` - - if(empty($bds_edit_pt_id) && empty($view)){ - if ($this->request->is('get')) { - - // Fetch inception data list - $data['inception_data_list'] = $this->policyTransactionModel->getInceptionTranctionListData( - $start_date, - $end_date, - $client_id, - $insurer_id, - $policy_type_id, - $date_type, - $issuer, - $status - ); - } else { - - $ids = $this->request->getPost('ids'); - $ids = array_filter(explode(',', $ids)); - $data['inception_data_list'] = $this->policyTransactionModel->getInceptionTranctionListData( - $start_date = 0, - $end_date = 0, - $client_id = 0, - $insurer_id = 0, - $policy_type_id = 0, - $date_type = 0, - $issuer = 0, - $status = 0, - $ids - ); - } - }else{ - $data['inception_data_list'] = []; - } + // List rows are loaded via server-side DataTables AJAX. + $data['inception_data_list'] = []; + $data['inception_filters'] = $this->buildInceptionFiltersFromRequest(); @@ -746,6 +696,136 @@ class PolicyTransactionController extends BaseController $this->loadLayout('policy_transaction_inception_list', $data); } + public function inceptionListDataTable() + { + if (!$this->request->isAJAX()) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Invalid request.', + ]); + } + + $draw = (int) ($this->request->getPost('draw') ?? 0); + $start = max(0, (int) ($this->request->getPost('start') ?? 0)); + $length = (int) ($this->request->getPost('length') ?? 10); + $search = trim((string) ($this->request->getPost('search')['value'] ?? '')); + + $filters = $this->buildInceptionFiltersFromRequest(); + $result = $this->policyTransactionModel->getInceptionTranctionListDataTable( + $draw, + $start, + $length, + $search, + $filters + ); + + $rows = []; + foreach ($result['data'] as $index => $row) { + $rows[] = $this->formatInceptionRowForDataTable($row, $start + $index + 1); + } + + return $this->response->setJSON([ + 'draw' => $result['draw'], + 'recordsTotal' => $result['recordsTotal'], + 'recordsFiltered' => $result['recordsFiltered'], + 'data' => $rows, + 'cache_expires_in_ms' => $result['cache_expires_in_ms'] ?? 300000, + ]); + } + + public function clearInceptionListCache() + { + if (!$this->request->isAJAX()) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Invalid request.', + ]); + } + + session()->set('inception_list_cache_version', time()); + + return $this->response->setJSON([ + 'status' => true, + 'message' => 'Inception list cache cleared.', + ]); + } + + protected function buildInceptionFiltersFromRequest(): array + { + $normalize = static function ($value) { + return (!isset($value) || $value === '' || $value === null) ? 0 : $value; + }; + + $ids = []; + if ($this->request->is('post')) { + $postIds = $this->request->getPost('ids') ?? ''; + $ids = array_filter(explode(',', (string) $postIds)); + } + + return [ + 'start_date' => $normalize($this->request->getGet('start_date') ?? $this->request->getPost('start_date')), + 'end_date' => $normalize($this->request->getGet('end_date') ?? $this->request->getPost('end_date')), + 'client_id' => $normalize($this->request->getGet('client_id') ?? $this->request->getPost('client_id')), + 'insurer_id' => $normalize($this->request->getGet('insurer_id') ?? $this->request->getPost('insurer_id')), + 'policy_type_id' => $normalize($this->request->getGet('policy_type_id') ?? $this->request->getPost('policy_type_id')), + 'date_type' => $normalize($this->request->getGet('date_type') ?? $this->request->getPost('date_type')), + 'issuer' => $normalize($this->request->getGet('issuer') ?? $this->request->getPost('issuer')), + 'status' => $normalize($this->request->getGet('status') ?? $this->request->getPost('status')), + 'ids' => $ids, + 'cache_version' => (int) (session()->get('inception_list_cache_version') ?? 1), + ]; + } + + protected function formatInceptionRowForDataTable(array $row, int $serialNo): array + { + $issuerMap = [1 => 'JIBS', 2 => 'Nhance']; + $issuingTypeMap = [1 => 'Fresh', 2 => 'Renewal', 3 => 'Roll Over']; + $clientTypeMap = [1 => 'Group', 2 => 'Individual']; + $policyStatusMap = [ + 'under_process' => 'Under Process', + 'client_pending' => 'Client Pending', + 'insurer_pending' => 'Insurer Pending', + 'co_insurer_pending' => 'Co-Insurer Pending', + 'tpa_pending' => 'TPA Pending', + 'validated' => 'Validated', + 'cancelled' => 'Cancelled', + 'instalment_pending' => 'Instalment Pending', + 'completed' => 'Completed', + 'lost' => 'Lost', + ]; + + $clientBranch = ((int) ($row['client_type'] ?? 0) === 2) + ? (($row['client_name'] ?? 'N/A') . ' - ' . (!empty($row['pan']) ? $row['pan'] : 'N/A')) + : (($row['client_short_name'] ?? 'N/A') . ' - ' . ($row['client_branch_name'] ?? 'N/A')); + + $editAction = 'Edit'; + $deleteAction = ''; + if ((int) get_role_id() === 5) { + $deleteAction = 'Delete'; + } + + $actionHtml = ''; + + return [ + 0 => $serialNo, + 1 => $issuerMap[$row['issuer'] ?? 2] ?? 'Nhance', + 2 => $issuingTypeMap[$row['issue_type'] ?? 0] ?? 'N/A', + 3 => $clientTypeMap[$row['client_type'] ?? 0] ?? 'N/A', + 4 => $clientBranch, + 5 => $row['insurer_short_name'] ?: 'N/A', + 6 => $row['policy_type'] ?: 'N/A', + 7 => $row['policy_no'] ?: 'N/A', + 8 => empty($row['policy_issue_date']) ? 'N/A' : date('d/m/Y', strtotime($row['policy_issue_date'])), + 9 => empty($row['policy_start_date']) ? 'N/A' : date('d/m/Y', strtotime($row['policy_start_date'])), + 10 => empty($row['policy_end_date']) ? 'N/A' : date('d/m/Y', strtotime($row['policy_end_date'])), + 11 => $row['emp_count'] ?: '0', + 12 => $row['dependent_count'] ?: '0', + 13 => $policyStatusMap[$row['status'] ?? ''] ?? 'N/A', + 14 => $row['user_name'] ?: 'N/A', + 15 => $actionHtml, + ]; + } + public function viewInception2() { $bds_edit_pt_id = $this->request->getGet('pt_id') ?? null; @@ -921,6 +1001,8 @@ class PolicyTransactionController extends BaseController // policy Transaction Create function start public function createInceptionPolicy() { + session()->set('inception_list_cache_version', time()); + $post_data = $this->request->getPost(); $rules = [ @@ -2391,31 +2473,8 @@ class PolicyTransactionController extends BaseController 'policy_end_date' => 'Policy End Date', ]; - //filter datas - $start_date = $this->request->getGet('start_date'); - $end_date = $this->request->getGet('end_date'); - $client_id = $this->request->getGet('client_id'); - $insurer_id = $this->request->getGet('insurer_id'); - $policy_type_id = $this->request->getGet('policy_type_id'); - $date_type = $this->request->getGet('date_type'); - $issuer = $this->request->getGet('issuer'); - $status = $this->request->getGet('status'); - - $start_date = (!isset($start_date) || $start_date === '' || $start_date === null) ? 0 : $start_date; - $end_date = (!isset($end_date) || $end_date === '' || $end_date === null) ? 0 : $end_date; - - $client_id = (!isset($client_id) || $client_id === '' || $client_id === null) ? 0 : $client_id; - $insurer_id = (!isset($insurer_id) || $insurer_id === '' || $insurer_id === null) ? 0 : $insurer_id; - $policy_type_id = (!isset($policy_type_id) || $policy_type_id === '' || $policy_type_id === null) ? 0 : $policy_type_id; - $date_type = (!isset($date_type) || $date_type === '' || $date_type === null) ? 0 : $date_type; - $issuer = (!isset($issuer) || $issuer === '' || $issuer === null) ? 0 : $issuer; - $status = (!isset($status) || $status === '' || $status === null) ? 0 : $status; - - if($bds_edit_pt_id == null){ - $data['endorsement_data_list'] = $this->policyTransactionModel->getEndorsementTranctionListData($start_date, $end_date, $client_id, $insurer_id, $policy_type_id, $date_type, $issuer, $status); - }else{ - $data['endorsement_data_list'] = []; - } + $data['endorsement_data_list'] = []; + $data['endorsement_filters'] = $this->buildEndorsementFiltersFromRequest(); $data['client'] = $this->clientModel->where('is_active', 1)->findAll(); $data['policy_types'] = $this->policyTypeModel->where('is_active', 1)->findAll(); @@ -2434,6 +2493,134 @@ class PolicyTransactionController extends BaseController $this->loadLayout('policy_transaction_endorsement_list', $data); } + public function endorsementListDataTable() + { + if (!$this->request->isAJAX()) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Invalid request.', + ]); + } + + $draw = (int) ($this->request->getPost('draw') ?? 0); + $start = max(0, (int) ($this->request->getPost('start') ?? 0)); + $length = (int) ($this->request->getPost('length') ?? 10); + $search = trim((string) ($this->request->getPost('search')['value'] ?? '')); + + $filters = $this->buildEndorsementFiltersFromRequest(); + $result = $this->policyTransactionModel->getEndorsementTranctionListDataTable( + $draw, + $start, + $length, + $search, + $filters + ); + + $rows = []; + foreach ($result['data'] as $index => $row) { + $rows[] = $this->formatEndorsementRowForDataTable($row, $start + $index + 1); + } + + return $this->response->setJSON([ + 'draw' => $result['draw'], + 'recordsTotal' => $result['recordsTotal'], + 'recordsFiltered' => $result['recordsFiltered'], + 'data' => $rows, + 'cache_expires_in_ms' => $result['cache_expires_in_ms'] ?? 300000, + ]); + } + + public function clearEndorsementListCache() + { + if (!$this->request->isAJAX()) { + return $this->response->setStatusCode(400)->setJSON([ + 'status' => false, + 'message' => 'Invalid request.', + ]); + } + + session()->set('endorsement_list_cache_version', time()); + + return $this->response->setJSON([ + 'status' => true, + 'message' => 'Endorsement list cache cleared.', + ]); + } + + protected function buildEndorsementFiltersFromRequest(): array + { + $normalize = static function ($value) { + return (!isset($value) || $value === '' || $value === null) ? 0 : $value; + }; + + return [ + 'start_date' => $normalize($this->request->getGet('start_date') ?? $this->request->getPost('start_date')), + 'end_date' => $normalize($this->request->getGet('end_date') ?? $this->request->getPost('end_date')), + 'client_id' => $normalize($this->request->getGet('client_id') ?? $this->request->getPost('client_id')), + 'insurer_id' => $normalize($this->request->getGet('insurer_id') ?? $this->request->getPost('insurer_id')), + 'policy_type_id' => $normalize($this->request->getGet('policy_type_id') ?? $this->request->getPost('policy_type_id')), + 'date_type' => $normalize($this->request->getGet('date_type') ?? $this->request->getPost('date_type')), + 'issuer' => $normalize($this->request->getGet('issuer') ?? $this->request->getPost('issuer')), + 'status' => $normalize($this->request->getGet('status') ?? $this->request->getPost('status')), + 'cache_version' => (int) (session()->get('endorsement_list_cache_version') ?? 1), + ]; + } + + protected function formatEndorsementRowForDataTable(array $row, int $serialNo): array + { + $issuerMap = [1 => 'JIBS', 2 => 'Nhance']; + $policyStatusMap = [ + 'under_process' => 'Under Process', + 'client_pending' => 'Client Pending', + 'insurer_pending' => 'Insurer Pending', + 'co_insurer_pending' => 'Co-Insurer Pending', + 'tpa_pending' => 'TPA Pending', + 'validated' => 'Validated', + 'cancelled' => 'Cancelled', + 'instalment_pending' => 'Instalment Pending', + 'completed' => 'Completed', + ]; + $actionTypeMap = [ + 'addition' => 'Addition', + 'deletion' => 'Deletion', + 'addition_deletion' => 'Addition & Deletion', + 'si_enhancement' => 'SI Enhancement', + 'combo_a_d_si' => 'Combo A, D & SI', + 'correction' => 'Correction', + 'baby_addition' => 'Baby Addition', + 'policy_instalment' => 'Policy Instalment', + 'addition_inception' => 'Addition-Inception', + 'bds_correction' => 'BDS Correction', + 'policy_correction' => 'Policy Correction', + 'policy_cancellation' => 'Policy Cancellation', + ]; + + $editAction = 'Edit'; + $deleteAction = ''; + if ((int) get_role_id() === 5) { + $deleteAction = 'Delete'; + } + + $actionHtml = ''; + + return [ + 0 => $serialNo, + 1 => $issuerMap[$row['issuer'] ?? 0] ?? 'N/A', + 2 => $row['client_short_name'] ?: 'N/A', + 3 => $row['client_branch_name'] ?: 'N/A', + 4 => $row['insurer_short_name'] ?: 'N/A', + 5 => $row['policy_type'] ?: 'N/A', + 6 => $actionTypeMap[$row['action_type'] ?? ''] ?? 'N/A', + 7 => $row['endorsement_no'] ?: 'N/A', + 8 => empty($row['data_received_date']) ? 'N/A' : date('d/m/Y', strtotime($row['data_received_date'])), + 9 => $row['emp_count'] ?: '0', + 10 => $row['dependent_count'] ?: '0', + 11 => empty($row['policy_issue_date']) ? 'N/A' : date('d/m/Y', strtotime($row['policy_issue_date'])), + 12 => $policyStatusMap[$row['status'] ?? ''] ?? 'N/A', + 13 => $actionHtml, + ]; + } + public function viewEndorsement2() { // echo '
';
@@ -2530,6 +2717,8 @@ class PolicyTransactionController extends BaseController
 
     public function createEndorsementPolicy()
     {
+        session()->set('endorsement_list_cache_version', time());
+
         // $id = $this->request->getPost('id');
         $rules = [
                 // ==========================================
@@ -3793,65 +3982,230 @@ class PolicyTransactionController extends BaseController
         $data['users'] = $this->userModel->where('is_active', 1)->findAll();
         $data['policy_count'] = $this->policyTransactionModel->where('is_active', 1)->countAllResults();
 
-
-        //filter datas
-        $start_date = $this->request->getGet('start_date');
-        $end_date = $this->request->getGet('end_date');
-        $client_id = $this->request->getGet('client_id');
-        $insurer_id = $this->request->getGet('insurer_id');
-        $policy_type_id = $this->request->getGet('policy_type_id');
-        $date_type = $this->request->getGet('date_type');
-        $issuer = $this->request->getGet('issuer');
-        $client_branch_id = $this->request->getGet('client_branch_id');
-        $insurer_branch_id = $this->request->getGet('insurer_branch_id');
-        $client_policy_id = $this->request->getGet('client_policy_id');
-        $user_id = $this->request->getGet('user_id');
-
-        if ($date_type == 'statement_month') {
-            $start_date = (string)date('Y-m-01', strtotime($start_date));
-            $end_date = (string)date('Y-m-31', strtotime($end_date));
-        }
-
-        // dd($start_date, $end_date, $date_type);
-
-        $start_date = (!isset($start_date) || $start_date === '' || $start_date === null) ? 0 : $start_date;
-        $end_date = (!isset($end_date) || $end_date === '' || $end_date === null) ? 0 : $end_date;
-
-        $client_id = (!isset($client_id) || $client_id === '' || $client_id === null) ? 0 : $client_id;
-        $insurer_id = (!isset($insurer_id) || $insurer_id === '' || $insurer_id === null) ? 0 : $insurer_id;
-        $policy_type_id = (!isset($policy_type_id) || $policy_type_id === '' || $policy_type_id === null) ? 0 : $policy_type_id;
-        $date_type = (!isset($date_type) || $date_type === '' || $date_type === null) ? 0 : $date_type;
-        $issuer = (!isset($issuer) || $issuer === '' || $issuer === null) ? 0 : $issuer;
-        $client_branch_id = (!isset($client_branch_id) || $client_branch_id === '' || $client_branch_id === null) ? 0 : $client_branch_id;
-        $insurer_branch_id = (!isset($insurer_branch_id) || $insurer_branch_id === '' || $insurer_branch_id === null) ? 0 : $insurer_branch_id;
-        $client_policy_id = (!isset($client_policy_id) || $client_policy_id === '' || $client_policy_id === null) ? 0 : $client_policy_id;
-        $user_id = (!isset($user_id) || $user_id === '' || $user_id === null) ? 0 : $user_id;
-        if ($this->request->is('post')) {
-            $request_post_data   = $this->request->getPost();
-            $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
-            $isFromDashboard = $sanitized_post_data["is_dashboard"];
-
-            if (isset($isFromDashboard) && !empty($isFromDashboard) && $isFromDashboard == 1) {
-                $ids = $sanitized_post_data['ids'];
-
-                $ids = array_filter(explode(',', $ids));
-
-                if (!empty($ids)) {
-                    $idsStr = implode(',', array_map('intval', $ids)); // sanitize IDs to be integers
-                    $where = "policy_transaction.id IN ($idsStr)";
-                } else {
-                    $where = []; // No valid IDs, return empty result
-                }
-            }
-            // dd($ids);
-        }
-        //Actual data for the list
-        $data['report_list'] = $this->policyTransactionModel->getBDSReportList($start_date, $end_date, $client_id, $insurer_id, $policy_type_id, $date_type, $issuer, $client_branch_id, $insurer_branch_id, $client_policy_id, $user_id, isset($where) ? $where : '');
+        // List data loaded via server-side DataTables AJAX
+        $data['report_list'] = [];
+        $data['bds_filters'] = $this->buildBDSReportFiltersFromRequest();
         // dd($data);
 
         $this->loadLayout('report_bds_filter', $data);
     }
 
+    /**
+     * Server-side DataTables endpoint for BDS report list.
+     */
+    public function reportBDSDataTable()
+    {
+        if (!$this->request->isAJAX()) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status'  => false,
+                'message' => 'Invalid request.',
+            ]);
+        }
+
+        $draw   = (int) ($this->request->getPost('draw') ?? 0);
+        $start  = max(0, (int) ($this->request->getPost('start') ?? 0));
+        $length = (int) ($this->request->getPost('length') ?? 10);
+        $search = trim((string) ($this->request->getPost('search')['value'] ?? ''));
+
+        $filters = $this->buildBDSReportFiltersFromRequest();
+
+        $result = $this->policyTransactionModel->getBDSReportListDataTable(
+            $draw,
+            $start,
+            $length,
+            $search,
+            $filters
+        );
+
+        $serialStart = $start + 1;
+        $data        = [];
+
+        foreach ($result['data'] as $index => $row) {
+            $data[] = $this->formatBdsReportRowForDataTable($row, $serialStart + $index);
+        }
+
+        return $this->response->setJSON([
+            'draw'            => $result['draw'],
+            'recordsTotal'    => $result['recordsTotal'],
+            'recordsFiltered' => $result['recordsFiltered'],
+            'data'            => $data,
+            'totals'          => $result['totals'],
+        ]);
+    }
+
+    public function clearReportBDSCache()
+    {
+        if (!$this->request->isAJAX()) {
+            return $this->response->setStatusCode(400)->setJSON([
+                'status'  => false,
+                'message' => 'Invalid request.',
+            ]);
+        }
+
+        session()->set('bds_report_cache_version', time());
+
+        return $this->response->setJSON([
+            'status'  => true,
+            'message' => 'BDS report cache cleared.',
+        ]);
+    }
+
+    /**
+     * Normalize BDS report filter params from the current request.
+     */
+    protected function buildBDSReportFiltersFromRequest(): array
+    {
+        $start_date        = $this->request->getGet('start_date') ?? $this->request->getPost('start_date');
+        $end_date          = $this->request->getGet('end_date') ?? $this->request->getPost('end_date');
+        $client_id         = $this->request->getGet('client_id') ?? $this->request->getPost('client_id');
+        $insurer_id        = $this->request->getGet('insurer_id') ?? $this->request->getPost('insurer_id');
+        $policy_type_id    = $this->request->getGet('policy_type_id') ?? $this->request->getPost('policy_type_id');
+        $date_type         = $this->request->getGet('date_type') ?? $this->request->getPost('date_type');
+        $issuer            = $this->request->getGet('issuer') ?? $this->request->getPost('issuer');
+        $client_branch_id  = $this->request->getGet('client_branch_id') ?? $this->request->getPost('client_branch_id');
+        $insurer_branch_id = $this->request->getGet('insurer_branch_id') ?? $this->request->getPost('insurer_branch_id');
+        $client_policy_id  = $this->request->getGet('client_policy_id') ?? $this->request->getPost('client_policy_id');
+        $user_id           = $this->request->getGet('user_id') ?? $this->request->getPost('user_id');
+
+        if ($date_type == 'statement_month' && $start_date && $end_date) {
+            $start_date = (string) date('Y-m-01', strtotime($start_date));
+            $end_date   = (string) date('Y-m-31', strtotime($end_date));
+        }
+
+        $normalize = static function ($value) {
+            return (!isset($value) || $value === '' || $value === null) ? 0 : $value;
+        };
+
+        $where = '';
+        if ($this->request->is('post')) {
+            $request_post_data   = $this->request->getPost();
+            $sanitized_post_data = sanitizeInputArrayAdvanced($request_post_data);
+            $isFromDashboard     = $sanitized_post_data['is_dashboard'] ?? null;
+
+            if (isset($isFromDashboard) && !empty($isFromDashboard) && (int) $isFromDashboard === 1) {
+                $ids = array_filter(explode(',', $sanitized_post_data['ids'] ?? ''));
+
+                if (!empty($ids)) {
+                    $idsStr = implode(',', array_map('intval', $ids));
+                    $where  = "policy_transaction.id IN ($idsStr)";
+                } else {
+                    $where = [];
+                }
+            }
+        }
+
+        return [
+            'start_date'        => $normalize($start_date),
+            'end_date'          => $normalize($end_date),
+            'client_id'         => $normalize($client_id),
+            'insurer_id'        => $normalize($insurer_id),
+            'policy_type_id'    => $normalize($policy_type_id),
+            'date_type'         => $normalize($date_type),
+            'issuer'            => $normalize($issuer),
+            'client_branch_id'  => $normalize($client_branch_id),
+            'insurer_branch_id' => $normalize($insurer_branch_id),
+            'client_policy_id'  => $normalize($client_policy_id),
+            'user_id'           => $normalize($user_id),
+            'where'             => $where,
+            'cache_version'     => (int) (session()->get('bds_report_cache_version') ?? 1),
+        ];
+    }
+
+    /**
+     * Format a single BDS report row for DataTables output.
+     */
+    protected function formatBdsReportRowForDataTable(array $row, int $serialNo): array
+    {
+        $hasIrda      = ((float) ($row['total_irda_amt'] ?? 0)) != 0.0;
+        $actionType  = strtolower((string) ($row['action_type'] ?? ''));
+        $editBaseUrl = $actionType === 'policy'
+            ? base_url('policy_tranction/inception/list')
+            : base_url('policy_tranction/endorsement/list');
+        $editLink    = $editBaseUrl . '?pt_id=' . ($row['id'] ?? '');
+        $totalIrdaAmt = $row['total_irda_amt'] ?? '0.00';
+        $unbilled     = isset($row['unbilled_amount']) ? number_format((float) $row['unbilled_amount'], 2, '.', '') : '0.00';
+
+        $fmtDate = static function ($value) {
+            return empty($value) ? 'N/A' : change_date_format($value, 'Y-m-d', 'd/m/Y');
+        };
+
+        $fmtNum = static function ($value, int $decimals = 2) {
+            return number_format((float) ($value ?? 0), $decimals, '.', '');
+        };
+
+        $na = static function ($value) {
+            return ($value !== null && $value !== '') ? $value : 'N/A';
+        };
+
+        $cells = [
+            $serialNo . '   ',
+            $na($row['user_name'] ?? null),
+            $na($row['policy_issue_month'] ?? null),
+            $na($row['revenue_type'] ?? null),
+            $na($row['client_type'] ?? null),
+            $na($row['client_name'] ?? null),
+            $na($row['action_type'] ?? null),
+            $na($row['policy_type'] ?? null),
+            $na($row['bap'] ?? null),
+            $na($row['vehicle_no'] ?? null),
+            $na($row['policy_no'] ?? null),
+            $na($row['endorsement_no'] ?? null),
+            $na($row['insurer_branch_name'] ?? null),
+            $fmtDate($row['endorse_eff_date'] ?? null),
+            $fmtDate($row['policy_start_date'] ?? null),
+            $fmtDate($row['policy_end_date'] ?? null),
+            $na($row['ref'] ?? null),
+            $na($row['remarks'] ?? null),
+            $hasIrda ? ($row['bp_amt'] ?: '0.00') : '0.00',
+            $hasIrda ? ($row['tp_or_ter'] ?: '0.00') : '0.00',
+            $hasIrda ? ($row['premium_wo_gst'] ?: '0.00') : '0.00',
+            $hasIrda ? ($row['total_premium'] ?: '0.00') : '0.00',
+            ($hasIrda ? ($row['agreed_bp_per'] ?: '0.00') : '0.00') . '%',
+            ($hasIrda ? ($row['agreed_tp_or_ter_per'] ?: '0.00') : '0.00') . '%',
+            isset($row['reward']) ? $row['reward'] : '0.00',
+            '' . $totalIrdaAmt . '',
+            '' . (empty($row['billed_amt']) ? '0.00' : $row['billed_amt']) . '',
+            '' . $unbilled . '',
+            $na($row['salse_person_name'] ?? null),
+            $na($row['service_person_name'] ?? null),
+            $na($row['nhance_branch'] ?? null),
+            $na($row['installment'] ?? null),
+            $fmtDate($row['data_received_date'] ?? null),
+            $fmtDate($row['renewal_date'] ?? null),
+            $row['co_share'] ?? 'No',
+            $row['bro_payable_by'] ?? 'No',
+            $na($row['salse_manager_name'] ?? null),
+            $na($row['service_manager_name'] ?? null),
+            $na($row['service_branch'] ?? null),
+            $fmtDate($row['rollover_date'] ?? null),
+            $na($row['policy_holder_name'] ?? null),
+            $row['same_as_proposer'] ?? 'No',
+            $na($row['follower_policy_no'] ?? null),
+            $fmtNum($row['co_share_per'] ?? 0),
+            $fmtNum($row['non_comm_per_amt'] ?? 0),
+            $fmtNum($row['bp_igst'] ?? 0),
+            $fmtNum($row['bp_sgst'] ?? 0),
+            $fmtNum($row['bp_cgst'] ?? 0),
+            $fmtNum($row['stamp_duty'] ?? 0),
+            $fmtNum($row['standerd_bp_per'] ?? 0),
+            $fmtNum($row['standerd_tp_per'] ?? 0),
+            $fmtNum($row['actual_bp_amt'] ?? 0),
+            $fmtNum($row['actual_tp_amt'] ?? 0),
+            $fmtNum($row['actual_bp_per'] ?? 0),
+            $fmtNum($row['actual_tp_per'] ?? 0),
+            $fmtNum($row['actual_tep_brokerage_amt'] ?? 0),
+            $fmtNum($row['actual_tp_brokerage_amt'] ?? 0),
+            $na($row['cd_ac_no'] ?? null),
+        ];
+
+        $rowData = ['DT_RowAttr' => ['data-id' => $row['pt_id'] ?? '']];
+        foreach ($cells as $index => $cell) {
+            $rowData[$index] = $cell;
+        }
+
+        return $rowData;
+    }
+
     public function reportVarience()
     {
         $data['tab_name'] = 'Variance Report';
@@ -5431,7 +5785,7 @@ class PolicyTransactionController extends BaseController
             }
 
             $this->myLogger->logme("error", "Data for BDS Installment " . json_encode($bdsInstallmentData));
-            // print_rr($bdsInstallmentData);die();
+            // dd($bdsInstallmentData);die();
 
             if (empty($bdsInstallmentData)) {
                 $this->myLogger->logme("error", "No Client Installment is Due in the 5th Day");
@@ -5439,31 +5793,91 @@ class PolicyTransactionController extends BaseController
                 return ['status' => false, 'message' => 'No data found', 'response' => []];
             }
 
+            $results       = [];
+            $successCount  = 0;
+            $failedCount   = 0;
+            $skippedCount  = 0;
+
             foreach ($bdsInstallmentData as $installmentData) {
+                $installmentId = $installmentData['id'] ?? 'unknown';
+                $leadId        = $installmentData['lead_id'] ?? 'unknown';
 
-                $mailData = $this->PrepareBDSMailData($installmentData);
-                $to_mail = $mailData['to_mail'];
-                $message = $mailData['message'];
-                $subject = $mailData['subject'];
+                try {
+                    $mailData = $this->PrepareBDSMailData($installmentData);
 
-                $this->myLogger->logme("error", "Installment Pending for" . $subject);
+                    if (empty($mailData) || empty($mailData['to_mail'])) {
+                        $this->myLogger->logme(
+                            'error',
+                            "Installment {$installmentId} (lead {$leadId}): skipped — no valid recipient emails"
+                        );
+                        $skippedCount++;
+                        $results[] = [
+                            'installment_id' => $installmentId,
+                            'lead_id'        => $leadId,
+                            'status'         => 'skipped',
+                            'message'        => 'No valid recipient emails',
+                        ];
+                        continue;
+                    }
 
-                $common = ['mail_type' => 'installment_amount_due_remainder_mail'];
+                    $to_mail = $mailData['to_mail'];
+                    $message = $mailData['message'];
+                    $subject = $mailData['subject'];
 
-                $res = MailHelper::send_email(['mail' => $to_mail, 'subject' => $subject, 'message' => $message, 'common' => $common]);
+                    $this->myLogger->logme("error", "Installment Pending for " . $subject);
 
-                $res = json_decode($res);
-                $this->myLogger->logme('error', 'res: ' . json_encode($res));
+                    $common = ['mail_type' => 'installment_amount_due_remainder_mail'];
 
-                if ($res->status == 'success') {
-                    CLI::write("Mail sent successfully to " . count($to_mail) . " recipients");
-                    return ['status' => true, 'message' => 'Mail sent successfully', 'response' => $res];
-                } else {
-                    CLI::write("Mail sent failed to " . count($to_mail) . " recipients");
-                    return ['status' => false, 'message' => 'Mail sent failed', 'response' => $res];
+                    $res = MailHelper::send_email(['mail' => $to_mail, 'subject' => $subject, 'message' => $message, 'common' => $common]);
+
+                    $res = json_decode($res);
+                    $this->myLogger->logme(
+                        'error',
+                        "Installment {$installmentId} (lead {$leadId}) mail result: " . json_encode($res)
+                    );
+
+                    if ($res->status == 'success') {
+                        $successCount++;
+                        CLI::write("Mail sent successfully for installment {$installmentId} to " . count($to_mail) . " recipients");
+                        $results[] = [
+                            'installment_id' => $installmentId,
+                            'lead_id'        => $leadId,
+                            'status'         => 'success',
+                        ];
+                    } else {
+                        $failedCount++;
+                        CLI::write("Mail failed for installment {$installmentId}");
+                        $results[] = [
+                            'installment_id' => $installmentId,
+                            'lead_id'        => $leadId,
+                            'status'         => 'failed',
+                            'response'       => $res,
+                        ];
+                    }
+                } catch (Exception $e) {
+                    $failedCount++;
+                    $this->myLogger->logme(
+                        'error',
+                        "Installment {$installmentId} (lead {$leadId}) exception: " . $e->getMessage()
+                    );
+                    $results[] = [
+                        'installment_id' => $installmentId,
+                        'lead_id'        => $leadId,
+                        'status'         => 'failed',
+                        'message'        => $e->getMessage(),
+                    ];
                 }
             }
 
+            $totalProcessed = count($bdsInstallmentData);
+            $summaryMessage = "Processed {$totalProcessed} installment(s): {$successCount} sent, {$failedCount} failed, {$skippedCount} skipped";
+
+            return [
+                'status'   => $successCount > 0,
+                'message'  => $summaryMessage,
+                'response' => $results,
+            ];
+
         } catch (Exception $e) {
             $this->myLogger->logme('error', 'Exception: ' . $e->getMessage() . 'Page: ' . $e->getFile() . ' Line: ' . $e->getLine());
         }
@@ -5474,44 +5888,63 @@ class PolicyTransactionController extends BaseController
 
         try {
 
-            $installment_amount =   $data['installment_amount'];
-            $payment_date       =   date('d-m-Y', strtotime($data['payment_date']));
-            $client             =   $data['client_name'];
-            $branch             =   $data['branch_name'];
-            $sales_person_mail  =   $data['sales_person'];
-            $heads              =   $data['heads'];
-            $admins             =   $data['admins'];
-            $buisness_team      =   $data['buisness_team'];
-            $policy_no          =   $data['policy_no'];
-            $client_short_name  =   isset($data['short_name']) && $data['short_name'] != null ? $data['short_name'] : $data['client_name'];
+            helper('excel_util_helper');
 
-            $subject = "Installment Amount Due For Client - {$client_short_name} - Policy NO({$policy_no}) is Due On - {$payment_date}";
+            $installment_amount = (float) ($data['installment_amount'] ?? 0);
+            $payment_date       = date('d-m-Y', strtotime($data['payment_date']));
+            $client             = $data['client_name'];
+            $branch             = $data['branch_name'];
+            $sales_person_mail  = $data['sales_person'];
+            $policy_no          = trim((string) ($data['policy_no'] ?? ''));
+            $client_short_name  = isset($data['short_name']) && $data['short_name'] != null ? $data['short_name'] : $data['client_name'];
+            $formatted_amount   = '₹ ' . formatIndianCurrency(number_format($installment_amount, 2, '.', ''));
+            $is_overdue         = strtotime($data['payment_date']) < strtotime(date('Y-m-d'));
+            $policy_label       = $policy_no !== '' ? "Policy #{$policy_no}" : 'Policy Pending';
+            $policy_period      = '';
 
-            $message = "Policy Installment for Client - {$client}, Branch - {$branch} is Due on {$payment_date} 
-            with amount of {$installment_amount}.
- Policy No : {$policy_no}"; + if (!empty($data['policy_start_date']) && !empty($data['policy_end_date'])) { + $policy_period = date('d-m-Y', strtotime($data['policy_start_date'])) + . ' to ' + . date('d-m-Y', strtotime($data['policy_end_date'])); + } + $subject = $is_overdue + ? "Overdue Installment Reminder - {$client_short_name} | {$policy_label} | Due {$payment_date}" + : "Installment Payment Reminder - {$client_short_name} | {$policy_label} | Due {$payment_date}"; + + $message = view('bds_installment_reminder_email_template', [ + 'client_name' => $client, + 'branch_name' => $branch, + 'policy_no' => $policy_no !== '' ? $policy_no : 'Not Assigned', + 'policy_type' => $data['policy_type'] ?? '', + 'insurer_name' => $data['insurer_name'] ?? '', + 'policy_period' => $policy_period, + 'payment_date' => $payment_date, + 'installment_amount' => $formatted_amount, + 'is_overdue' => $is_overdue, + 'generated_on' => date('d-m-Y H:i'), + ]); $contactPersonEmail = trim((string) ($data['contact_person_email'] ?? '')); $to_mail = array_merge( - array_column($heads, 'email'), - array_column($admins, 'email'), - array_column($buisness_team, 'email'), + [$data['acm_email']], [$sales_person_mail], $contactPersonEmail !== '' ? [$contactPersonEmail] : [] ); - $to_mail = array_values(array_unique(array_filter($to_mail))); + $to_mail = array_values(array_unique(array_filter($to_mail, static function ($email) { + return is_string($email) && filter_var(trim($email), FILTER_VALIDATE_EMAIL); + }))); $this->myLogger->logme("error", "Selected To Address : " . json_encode($to_mail)); - // dd($to_mail); return [ 'to_mail' => $to_mail, 'message' => $message, 'subject' => $subject ]; + } catch (Exception $e) { $this->myLogger->logme('error', 'Exception: ' . $e->getMessage() . 'Page: ' . $e->getFile() . ' Line: ' . $e->getLine()); diff --git a/app/Libraries/TPAClaimsImportServices/AbhiClaimImportService.php b/app/Libraries/TPAClaimsImportServices/AbhiClaimImportService.php index 81a01aec..08da325e 100644 --- a/app/Libraries/TPAClaimsImportServices/AbhiClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/AbhiClaimImportService.php @@ -242,20 +242,6 @@ class AbhiClaimImportService extends BaseTpaClaimImportService $item[$value] = change_date_format($item[$value] ?? null, 'm/d/Y h:i:s A', 'Y-m-d'); } - $params = [ - 'doa' => $item['doa'] ?? null, - 'member_code' => $item['member_code'] ?? null, - 'claimed_amount' => $item['claimed_amount'] ?? null, - 'healthcard_id' => $item['healthcard_id'] ?? null - ]; - - $is_duplicate = $this->checkDuplicateTpaClaim('claims_dump_abhi', $params); - - if ($is_duplicate) { - $item = []; - continue; - } - $item['file_id'] = $file_id ?? null; $item['client_id'] = $file_data['client_id'] ?? null; $item['client_policy_id'] = $file_data['client_policy_id'] ?? null; @@ -276,7 +262,7 @@ class AbhiClaimImportService extends BaseTpaClaimImportService $tpaClaimDumpData = $this->getTpaClaimDumpData('claims_dump_abhi', ['file_id' => $file_id]); if (empty($tpaClaimDumpData)) { - return ['status' => false, 'message' => "No data to insert in TICKET MASTER"]; + return $this->emptyClaimMasterMappingResponse('claims_dump_abhi', (int) $file_id); } @@ -302,6 +288,7 @@ class AbhiClaimImportService extends BaseTpaClaimImportService $mapped = []; $rejecetd_reason = []; + $status_update_array = []; foreach ($tpaClaimDumpData as $row) { @@ -312,11 +299,16 @@ class AbhiClaimImportService extends BaseTpaClaimImportService 'tpa_no' => $row['healthcard_id'] ?? null ]; - $isduplicate = $this->checkDublicateTicketMasterClaim($params); + $newStatusId = $this->checkStatusMapping($this->statusMapping, $row['claim_status'] ?? '') ?? 61; + $existingTicket = $this->getExistingTicketMasterClaim($params); - if ($isduplicate) { - $reason = "This claim already exists in our system."; - $rejecetd_reason[] = ["id" => $row['id'], "master_reject_reason" => $reason]; + if ($this->handleExistingTicketStatusUpdate( + $existingTicket, + $newStatusId, + (int) $row['id'], + $status_update_array, + $rejecetd_reason + )) { continue; } @@ -358,7 +350,7 @@ class AbhiClaimImportService extends BaseTpaClaimImportService } // Meta fields - $item['claim_status_id'] = $this->checkStatusMapping($this->statusMapping, $row['claim_status']); + $item['claim_status_id'] = $newStatusId; $item['file_id'] = $file_id; $item['claim_dump_ref_id'] = $row['id']; $item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null; @@ -372,7 +364,12 @@ class AbhiClaimImportService extends BaseTpaClaimImportService $mapped[] = $item; } - return ['status' => true, 'mapped_array' => $mapped, 'rejected_reason_array' => $rejecetd_reason]; + return [ + 'status' => true, + 'mapped_array' => $mapped, + 'rejected_reason_array' => $rejecetd_reason, + 'status_update_array' => $status_update_array, + ]; } catch (\Throwable $th) { $errorData = [ diff --git a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php index 467fceba..d5df02fc 100644 --- a/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/BaseTpaClaimImportService.php @@ -116,16 +116,28 @@ abstract class BaseTpaClaimImportService // Check if mapping failed if (!$ticketMasterData['status']) { + // Already-processed dump rows (ticket_id linked) must not be deleted as a "failure". + if (!empty($ticketMasterData['already_processed'])) { + $this->db->transCommit(); + return [ + 'status' => true, + 'message' => $ticketMasterData['message'] ?? 'Claim dump already processed for this file.', + ]; + } + $this->rollbackAndCleanupClaimDumpData($file_id); return $ticketMasterData; } $message = ''; $hasExecutedTask = false; - $status = true; + $hasInserts = !empty($ticketMasterData['mapped_array']); + $hasExistingTicketUpdates = !empty($ticketMasterData['status_update_array']); + $hasRejectedReasons = !empty($ticketMasterData['rejected_reason_array']); + $hasExistingTicketLinks = $this->hasExistingTicketLinkUpdates($ticketMasterData['rejected_reason_array'] ?? []); // Process Mapped Data - if (!empty($ticketMasterData['mapped_array'])) { + if ($hasInserts) { $insert_res = $this->importClaimMaster($ticketMasterData['mapped_array']); if (!$insert_res) { return $this->failTicketMasterInsert($file_id, 'Ticket Master Claim bulk insert failed'); @@ -137,20 +149,33 @@ abstract class BaseTpaClaimImportService $message .= 'Ticket Master Claim bulk insert success. '; $hasExecutedTask = true; - }else{ - $status = false; } - // Process Rejected Reasons - if (!empty($ticketMasterData['rejected_reason_array'])) { + // Update existing tickets: status and/or missing claim_dump_ref_id + if ($hasExistingTicketUpdates) { + $update_status_res = $this->updateExistingTicketStatuses($ticketMasterData['status_update_array']); + if (!$update_status_res) { + return $this->failTicketMasterInsert($file_id, 'Updating existing tickets failed'); + } + + $message .= 'Existing ticket updated successfully. '; + $hasExecutedTask = true; + } + + // Process Rejected Reasons (also writes ticket_id onto TPA dump rows for existing tickets) + if ($hasRejectedReasons) { $update_res = $this->updateTicketMasterRejectedReasonInTPATable($ticketMasterData['rejected_reason_array']); if (!$update_res) { return $this->failTicketMasterInsert($file_id, 'Updating rejected reasons failed'); } - - $message .= empty($ticketMasterData['mapped_array']) - ? 'Those employee or dependent not in our system. ' - : 'Ticket Master Claim rejected reason updated successfully. '; + + if ($hasExistingTicketLinks) { + $message .= 'Existing claim dump ticket_id linked successfully. '; + } elseif (!$hasInserts && !$hasExistingTicketUpdates) { + $message .= 'Those employee or dependent not in our system. '; + } else { + $message .= 'Ticket Master Claim rejected reason updated successfully. '; + } $hasExecutedTask = true; } @@ -162,6 +187,7 @@ abstract class BaseTpaClaimImportService // 2. Commit the transaction $this->db->transCommit(); + $status = $hasInserts || $hasExistingTicketUpdates || $hasExistingTicketLinks; if (!$status) { $this->cleanupClaimDumpData($file_id); } @@ -323,22 +349,110 @@ abstract class BaseTpaClaimImportService */ protected function checkDublicateTicketMasterClaim(array $param): bool { - $ticketMaster = new TicketMasterModel(); - $ticket_master_data = $ticketMaster - ->where('doa', $param['doa']) - ->where('tpa_no', $param['tpa_no']) - ->where('claim_amount', $param['claim_amount']) - ->where('emp_code', $param['emp_code']) - ->where('is_active', 1) - ->findAll(); + return $this->getExistingTicketMasterClaim($param) !== null; + } - if(count($ticket_master_data) > 0){ + /** + * Fetch existing ticket_master record matching claim identity keys. + */ + protected function getExistingTicketMasterClaim(array $param): ?array + { + $ticketMaster = new TicketMasterModel(); + $ticket = $ticketMaster + ->where('doa', $param['doa']) + ->where('tpa_no', $param['tpa_no']) + ->where('claim_amount', $param['claim_amount']) + ->where('emp_code', $param['emp_code']) + ->where('is_active', 1) + ->first(); + + return $ticket ?: null; + } + + /** + * Update existing ticket_master rows (status and/or claim_dump_ref_id). + */ + protected function updateExistingTicketStatuses(array $statusUpdates): bool + { + if (empty($statusUpdates)) { return true; } + $ticketMasterModel = new TicketMasterModel(); + return $ticketMasterModel->updateBatch($statusUpdates, 'id') !== false; + } + + /** + * True when rejected_reason rows include ticket_id links for existing tickets. + */ + protected function hasExistingTicketLinkUpdates(array $rejectedReasonArray): bool + { + foreach ($rejectedReasonArray as $row) { + if (!empty($row['ticket_id'])) { + return true; + } + } + return false; } + /** + * If ticket already exists: + * - update claim_status_id when changed + * - set claim_dump_ref_id on ticket_master when null + * - set ticket_id on TPA dump row when null (via rejected_reason_array) + * + * Returns true when the row was handled as an existing ticket (caller should continue). + */ + protected function handleExistingTicketStatusUpdate( + ?array $existingTicket, + int $newStatusId, + int $dumpRowId, + array &$statusUpdateArray, + array &$rejectedReasonArray + ): bool { + if (empty($existingTicket)) { + return false; + } + + $ticketId = $existingTicket['id']; + $currentStatusId = (int) ($existingTicket['claim_status_id'] ?? 0); + $statusChanged = $currentStatusId !== (int) $newStatusId; + $claimDumpRefIdMissing = empty($existingTicket['claim_dump_ref_id']); + + $ticketUpdate = ['id' => $ticketId]; + $reasons = []; + + if ($statusChanged) { + $ticketUpdate['claim_status_id'] = $newStatusId; + $reasons[] = 'status updated'; + } + + if ($claimDumpRefIdMissing) { + $ticketUpdate['claim_dump_ref_id'] = $dumpRowId; + $reasons[] = 'claim_dump_ref_id linked'; + } + + if (count($ticketUpdate) > 1) { + $statusUpdateArray[] = $ticketUpdate; + } + + if ($statusChanged || $claimDumpRefIdMissing) { + $reason = 'Existing claim ' . implode(' and ', $reasons) . '.'; + } else { + $reason = 'This claim already exists in our system.'; + } + + // Dump rows reaching here already have ticket_id NULL; link them to the existing ticket. + $rejectedReasonArray[] = [ + 'id' => $dumpRowId, + 'master_reject_reason' => $reason, + 'ticket_id' => $ticketId, + ]; + + return true; + } + /** * Dublicate check in the TPA specific table records */ @@ -365,6 +479,32 @@ abstract class BaseTpaClaimImportService ->getResultArray(); } + /** + * Response when no pending dump rows are left for ticket_master mapping. + * Marks already-processed files so cleanup does not delete linked dump rows. + */ + protected function emptyClaimMasterMappingResponse(string $table, int $fileId): array + { + $alreadyProcessed = $this->db->table($table) + ->where('file_id', $fileId) + ->where('is_active', 1) + ->groupStart() + ->where('ticket_id IS NOT NULL', null, false) + ->orWhere('master_reject_reason IS NOT NULL', null, false) + ->groupEnd() + ->countAllResults() > 0; + + if ($alreadyProcessed) { + return [ + 'status' => false, + 'already_processed' => true, + 'message' => 'Claim dump already processed for this file.', + ]; + } + + return ['status' => false, 'message' => 'No data to insert in TICKET MASTER']; + } + /** * Dublicate check in the TPA specific table records */ diff --git a/app/Libraries/TPAClaimsImportServices/FhplClaimImportService.php b/app/Libraries/TPAClaimsImportServices/FhplClaimImportService.php index 653043ef..e436be0a 100644 --- a/app/Libraries/TPAClaimsImportServices/FhplClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/FhplClaimImportService.php @@ -308,20 +308,6 @@ class FhplClaimImportService extends BaseTpaClaimImportService $item[$value] = change_date_format($item[$value] ?? null, 'd-M-y', 'Y-m-d'); } - $params = [ - 'admission_date' => $item['admission_date'] ?? null, - 'employee_id' => $item['employee_id'] ?? null, - 'claim_amount' => $item['claim_amount'] ?? null, - 'uhid_no' => $item['uhid_no'] ?? null - ]; - - $is_duplicate = $this->checkDuplicateTpaClaim('claims_dump_fhpl', $params); - - if ($is_duplicate) { - $item = []; - continue; - } - $item['file_id'] = $file_id ?? null; $item['client_id'] = $file_data['client_id'] ?? null; $item['client_policy_id'] = $file_data['client_policy_id'] ?? null; @@ -342,7 +328,7 @@ class FhplClaimImportService extends BaseTpaClaimImportService $tpaClaimDumpData = $this->getTpaClaimDumpData('claims_dump_fhpl', ['file_id' => $file_id]); if (empty($tpaClaimDumpData)) { - return ['status' => false, 'message' => "No data to insert in TICKET MASTER"]; + return $this->emptyClaimMasterMappingResponse('claims_dump_fhpl', (int) $file_id); } @@ -368,6 +354,7 @@ class FhplClaimImportService extends BaseTpaClaimImportService $mapped = []; $rejecetd_reason = []; + $status_update_array = []; foreach ($tpaClaimDumpData as $row) { @@ -378,11 +365,16 @@ class FhplClaimImportService extends BaseTpaClaimImportService 'tpa_no' => $row['uhid_no'] ?? null ]; - $isduplicate = $this->checkDublicateTicketMasterClaim($params); + $newStatusId = $this->checkStatusMapping($this->statusMapping, $row['current_claim_status'] ?? '') ?? 61; + $existingTicket = $this->getExistingTicketMasterClaim($params); - if ($isduplicate) { - $reason = "This claim already exists in our system."; - $rejecetd_reason[] = ["id" => $row['id'], "master_reject_reason" => $reason]; + if ($this->handleExistingTicketStatusUpdate( + $existingTicket, + $newStatusId, + (int) $row['id'], + $status_update_array, + $rejecetd_reason + )) { continue; } @@ -424,7 +416,7 @@ class FhplClaimImportService extends BaseTpaClaimImportService } // Meta fields - $item['claim_status_id'] = $this->checkStatusMapping($this->statusMapping, $row['current_claim_status']) ?? 61; + $item['claim_status_id'] = $newStatusId; $item['file_id'] = $file_id; $item['claim_dump_ref_id'] = $row['id']; $item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null; @@ -438,7 +430,12 @@ class FhplClaimImportService extends BaseTpaClaimImportService $mapped[] = $item; } - return ['status' => true, 'mapped_array' => $mapped, 'rejected_reason_array' => $rejecetd_reason]; + return [ + 'status' => true, + 'mapped_array' => $mapped, + 'rejected_reason_array' => $rejecetd_reason, + 'status_update_array' => $status_update_array, + ]; } catch (\Throwable $th) { diff --git a/app/Libraries/TPAClaimsImportServices/IciciClaimImportService.php b/app/Libraries/TPAClaimsImportServices/IciciClaimImportService.php index 15da3177..b4d61ebd 100644 --- a/app/Libraries/TPAClaimsImportServices/IciciClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/IciciClaimImportService.php @@ -223,20 +223,6 @@ class IciciClaimImportService extends BaseTpaClaimImportService $item[$value] = change_date_format($item[$value] ?? null); } - $params = [ - 'doa' => change_date_format($item['doa'] ?? '') ?? null, - 'employee_member_id' => $item['employee_member_id'] ?? null, - 'claimed_amount' => $item['claimed_amount'] ?? null, - 'uhid' => $item['uhid'] ?? null - ]; - - $is_duplicate = $this->checkDuplicateTpaClaim('claims_dump_icici', $params); - - if ($is_duplicate) { - $item = []; - continue; - } - $item['file_id'] = $file_id ?? null; $item['client_id'] = $file_data['client_id'] ?? null; $item['client_policy_id'] = $file_data['client_policy_id'] ?? null; @@ -257,7 +243,7 @@ class IciciClaimImportService extends BaseTpaClaimImportService $tpaClaimDumpData = $this->getTpaClaimDumpData('claims_dump_icici', ['file_id' => $file_id]); if (empty($tpaClaimDumpData)) { - return ['status' => false, 'message' => "No data to insert in TICKET MASTER"]; + return $this->emptyClaimMasterMappingResponse('claims_dump_icici', (int) $file_id); } @@ -283,21 +269,27 @@ class IciciClaimImportService extends BaseTpaClaimImportService $mapped = []; $rejecetd_reason = []; + $status_update_array = []; foreach ($tpaClaimDumpData as $row) { $params = [ 'doa' => change_date_format($row['doa'] ?? '') ?? null, 'emp_code' => $row['employee_member_id'] ?? null, - 'claim_amount' => $row['claim_amount'] ?? null, + 'claim_amount' => $row['claimed_amount'] ?? null, 'tpa_no' => $row['uhid'] ?? null ]; - $isduplicate = $this->checkDublicateTicketMasterClaim($params); + $newStatusId = $this->checkStatusMapping($this->statusMapping, $row['updated_status'] ?? '') ?? 61; + $existingTicket = $this->getExistingTicketMasterClaim($params); - if ($isduplicate) { - $reason = "This claim already exists in our system."; - $rejecetd_reason[] = ["id" => $row['id'], "master_reject_reason" => $reason]; + if ($this->handleExistingTicketStatusUpdate( + $existingTicket, + $newStatusId, + (int) $row['id'], + $status_update_array, + $rejecetd_reason + )) { continue; } @@ -339,7 +331,7 @@ class IciciClaimImportService extends BaseTpaClaimImportService } // Meta fields - $item['claim_status_id'] = $this->checkStatusMapping($this->statusMapping, $row['updated_status']) ?? 61; + $item['claim_status_id'] = $newStatusId; $item['claim_dump_ref_id'] = $row['id']; $item['file_id'] = $file_id; $item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null; @@ -353,7 +345,12 @@ class IciciClaimImportService extends BaseTpaClaimImportService $mapped[] = $item; } - return ['status' => true, 'mapped_array' => $mapped, 'rejected_reason_array' => $rejecetd_reason]; + return [ + 'status' => true, + 'mapped_array' => $mapped, + 'rejected_reason_array' => $rejecetd_reason, + 'status_update_array' => $status_update_array, + ]; } catch (\Throwable $th) { diff --git a/app/Libraries/TPAClaimsImportServices/MediAssistClaimImportService.php b/app/Libraries/TPAClaimsImportServices/MediAssistClaimImportService.php index a6ea7d0c..e2533924 100644 --- a/app/Libraries/TPAClaimsImportServices/MediAssistClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/MediAssistClaimImportService.php @@ -256,20 +256,6 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService $item[$dbColumn] = $value; } - $params = [ - 'date_of_admission' => change_date_format($item['date_of_admission'] ?? '') ?? null, - 'pribenef_employee_code' => $item['pribenef_employee_code'] ?? null, - 'claim_amount' => $item['claim_amount'] ?? null, - 'event_id' => $item['event_id'] ?? null - ]; - - $is_duplicate = $this->checkDuplicateTpaClaim('claims_dump_medi_assist', $params); - - if ($is_duplicate) { - $item = []; - continue; - } - $item['file_id'] = $file_id ?? null; $item['client_id'] = $file_data['client_id'] ?? null; $item['client_policy_id'] = $file_data['client_policy_id'] ?? null; @@ -290,7 +276,7 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService $tpaClaimDumpData = $this->getTpaClaimDumpData('claims_dump_medi_assist', ['file_id' => $file_id]); if (empty($tpaClaimDumpData)) { - return ['status' => false, 'message' => "No data to insert in TICKET MASTER"]; + return $this->emptyClaimMasterMappingResponse('claims_dump_medi_assist', (int) $file_id); } @@ -316,6 +302,7 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService $mapped = []; $rejecetd_reason = []; + $status_update_array = []; foreach ($tpaClaimDumpData as $row) { @@ -326,11 +313,16 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService 'tpa_no' => $row['event_id'] ?? null ]; - $isduplicate = $this->checkDublicateTicketMasterClaim($params); + $newStatusId = $this->checkStatusMapping($this->statusMapping, $row['claim_status'] ?? '') ?? 61; + $existingTicket = $this->getExistingTicketMasterClaim($params); - if ($isduplicate) { - $reason = "This claim already exists in our system."; - $rejecetd_reason[] = ["id" => $row['id'], "master_reject_reason" => $reason]; + if ($this->handleExistingTicketStatusUpdate( + $existingTicket, + $newStatusId, + (int) $row['id'], + $status_update_array, + $rejecetd_reason + )) { continue; } @@ -374,7 +366,7 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService // Meta fields $item['claim_dump_ref_id'] = $row['id']; $item['file_id'] = $file_id; - $item['claim_status_id'] = $this->checkStatusMapping($this->statusMapping, $row['claim_status']) ?? 61; + $item['claim_status_id'] = $newStatusId; $item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null; $item['created_by'] = $file_data['created_by'] ?? null; $item['claim_type'] = 1; @@ -386,7 +378,12 @@ class MediAssistClaimImportService extends BaseTpaClaimImportService $mapped[] = $item; } - return ['status' => true, 'mapped_array' => $mapped, 'rejected_reason_array' => $rejecetd_reason]; + return [ + 'status' => true, + 'mapped_array' => $mapped, + 'rejected_reason_array' => $rejecetd_reason, + 'status_update_array' => $status_update_array, + ]; } catch (\Throwable $th) { diff --git a/app/Libraries/TPAClaimsImportServices/RcareClaimImportService.php b/app/Libraries/TPAClaimsImportServices/RcareClaimImportService.php index 0b93f149..646a63e9 100644 --- a/app/Libraries/TPAClaimsImportServices/RcareClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/RcareClaimImportService.php @@ -208,20 +208,6 @@ class RcareClaimImportService extends BaseTpaClaimImportService $item[$value] = change_date_format($item[$value] ?? null, 'd-M-y', 'Y-m-d'); } - $params = [ - 'doa_opd_treatment_from' => $item['doa_opd_treatment_from'] ?? null, - 'employee_member_id' => $item['employee_member_id'] ?? null, - 'claimed_amount' => $item['claimed_amount'] ?? null, - 'uhid' => $item['uhid'] ?? null - ]; - - $is_duplicate = $this->checkDuplicateTpaClaim('claims_dump_reliance', $params); - - if ($is_duplicate) { - $item = []; - continue; - } - $item['file_id'] = $file_id ?? null; $item['client_id'] = $file_data['client_id'] ?? null; $item['client_policy_id'] = $file_data['client_policy_id'] ?? null; @@ -242,7 +228,7 @@ class RcareClaimImportService extends BaseTpaClaimImportService $tpaClaimDumpData = $this->getTpaClaimDumpData('claims_dump_reliance', ['file_id' => $file_id]); if (empty($tpaClaimDumpData)) { - return ['status' => false, 'message' => "No data to insert in TICKET MASTER"]; + return $this->emptyClaimMasterMappingResponse('claims_dump_reliance', (int) $file_id); } @@ -268,6 +254,7 @@ class RcareClaimImportService extends BaseTpaClaimImportService $mapped = []; $rejecetd_reason = []; + $status_update_array = []; foreach ($tpaClaimDumpData as $row) { @@ -278,11 +265,16 @@ class RcareClaimImportService extends BaseTpaClaimImportService 'tpa_no' => $row['uhid'] ?? null ]; - $isduplicate = $this->checkDublicateTicketMasterClaim($params); + $newStatusId = $this->checkStatusMapping($this->statusMapping, $row['final_status'] ?? '') ?? 61; + $existingTicket = $this->getExistingTicketMasterClaim($params); - if ($isduplicate) { - $reason = "This claim already exists in our system."; - $rejecetd_reason[] = ["id" => $row['id'], "master_reject_reason" => $reason]; + if ($this->handleExistingTicketStatusUpdate( + $existingTicket, + $newStatusId, + (int) $row['id'], + $status_update_array, + $rejecetd_reason + )) { continue; } @@ -324,7 +316,7 @@ class RcareClaimImportService extends BaseTpaClaimImportService } // Meta fields - $item['claim_status_id'] = $this->checkStatusMapping($this->statusMapping, $row['final_status']) ?? 61; + $item['claim_status_id'] = $newStatusId; $item['file_id'] = $file_id; $item['created_by'] = $file_data['created_by'] ?? null; $item['claim_dump_ref_id'] = $row['id']; @@ -338,7 +330,12 @@ class RcareClaimImportService extends BaseTpaClaimImportService $mapped[] = $item; } - return ['status' => true, 'mapped_array' => $mapped, 'rejected_reason_array' => $rejecetd_reason]; + return [ + 'status' => true, + 'mapped_array' => $mapped, + 'rejected_reason_array' => $rejecetd_reason, + 'status_update_array' => $status_update_array, + ]; } catch (\Throwable $th) { diff --git a/app/Libraries/TPAClaimsImportServices/VidalClaimImportService.php b/app/Libraries/TPAClaimsImportServices/VidalClaimImportService.php index a0876b92..fd628cc9 100644 --- a/app/Libraries/TPAClaimsImportServices/VidalClaimImportService.php +++ b/app/Libraries/TPAClaimsImportServices/VidalClaimImportService.php @@ -455,20 +455,6 @@ class VidalClaimImportService extends BaseTpaClaimImportService $item[$dbColumn] = $value; } - $params = [ - 'date_of_admission' => change_date_format($item['date_of_admission'] ?? '') ?? null, - 'employee_number' => $item['employee_number'] ?? null, - 'claim_amount' => $item['claim_amount'] ?? null, - 'primary_policy_holder_card_id' => $item['primary_policy_holder_card_id'] ?? null - ]; - - $is_duplicate = $this->checkDuplicateTpaClaim('claims_dump_vidal', $params); - - if ($is_duplicate) { - $item = []; - continue; - } - $item['file_id'] = $file_id ?? null; $item['client_id'] = $file_data['client_id'] ?? null; $item['client_policy_id'] = $file_data['client_policy_id'] ?? null; @@ -489,7 +475,7 @@ class VidalClaimImportService extends BaseTpaClaimImportService $tpaClaimDumpData = $this->getTpaClaimDumpData('claims_dump_vidal', ['file_id' => $file_id]); if (empty($tpaClaimDumpData)) { - return ['status' => false, 'message' => "No data to insert in TICKET MASTER"]; + return $this->emptyClaimMasterMappingResponse('claims_dump_vidal', (int) $file_id); } @@ -515,6 +501,7 @@ class VidalClaimImportService extends BaseTpaClaimImportService $mapped = []; $rejecetd_reason = []; + $status_update_array = []; foreach ($tpaClaimDumpData as $row) { @@ -525,11 +512,16 @@ class VidalClaimImportService extends BaseTpaClaimImportService 'tpa_no' => $row['primary_policy_holder_card_id'] ?? null ]; - $isduplicate = $this->checkDublicateTicketMasterClaim($params); + $newStatusId = $this->checkStatusMapping($this->statusMapping, $row['claim_status'] ?? '') ?? 61; + $existingTicket = $this->getExistingTicketMasterClaim($params); - if ($isduplicate) { - $reason = "This claim already exists in our system."; - $rejecetd_reason[] = ["id" => $row['id'], "master_reject_reason" => $reason]; + if ($this->handleExistingTicketStatusUpdate( + $existingTicket, + $newStatusId, + (int) $row['id'], + $status_update_array, + $rejecetd_reason + )) { continue; } @@ -565,7 +557,7 @@ class VidalClaimImportService extends BaseTpaClaimImportService } // Meta fields - $item['claim_status_id'] = $this->checkStatusMapping($this->statusMapping, $row['claim_status']) ?? 61; + $item['claim_status_id'] = $newStatusId; $item['file_id'] = $file_id; $item['claim_dump_ref_id'] = $row['id']; $item['claim_dump_date'] = $file_data['claim_dump_date'] ?? null; @@ -579,7 +571,12 @@ class VidalClaimImportService extends BaseTpaClaimImportService $mapped[] = $item; } - return ['status' => true, 'mapped_array' => $mapped, 'rejected_reason_array' => $rejecetd_reason]; + return [ + 'status' => true, + 'mapped_array' => $mapped, + 'rejected_reason_array' => $rejecetd_reason, + 'status_update_array' => $status_update_array, + ]; } catch (\Throwable $th) { diff --git a/app/Models/BdsPlacementModel.php b/app/Models/BdsPlacementModel.php index 341e16a2..ff1fbc61 100644 --- a/app/Models/BdsPlacementModel.php +++ b/app/Models/BdsPlacementModel.php @@ -135,37 +135,56 @@ class BdsPlacementModel extends Model { /** @var BdsConfig $config */ $config = config(BdsConfig::class); - // print_r($config);die(); if (!$config->shouldFetchInstallmentRemindersToday()) { return []; } - $heads = $this->db->table('user_profiles') - ->select('email')->where(['role' => 5, 'is_active' => 1]) - ->get()->getResultArray(); + // $heads = $this->db->table('user_profiles') + // ->select('email')->where(['role' => 5, 'is_active' => 1]) + // ->get()->getResultArray(); - $admins = $this->db->table('user_profiles') - ->select('email')->where(['role' => 1, 'is_active' => 1]) - ->get()->getResultArray(); + // $admins = $this->db->table('user_profiles') + // ->select('email')->where(['role' => 1, 'is_active' => 1]) + // ->get()->getResultArray(); - $businessTeam = $this->db->table("user_teams ut") - ->select("up.email") - ->join('user_profiles up', 'up.id = ut.user_id AND up.is_active = 1') - ->where(["ut.team_id" => 7, "ut.is_active" => 1]) - ->get()->getResultArray(); + // $businessTeam = $this->db->table("user_teams ut") + // ->select("up.email") + // ->join('user_profiles up', 'up.id = ut.user_id AND up.is_active = 1') + // ->where(["ut.team_id" => 7, "ut.is_active" => 1]) + // ->get()->getResultArray(); $builder = $this->db->table("lead_installment_payment_details lipd") - ->select("lipd.*, COALESCE(ct.client_name, leads.client_name) as client_name, COALESCE(ct.short_name, leads.client_short_name) as short_name, COALESCE(cb.branch_name, leads.branch_name) as branch_name, leads.salse_person_id, leads.contact_person_email, cp.policy_no") + ->select(" + lipd.*, + COALESCE(ct.client_name, leads.client_name) as client_name, + COALESCE(ct.short_name, leads.client_short_name) as short_name, + COALESCE(cb.branch_name, leads.branch_name) as branch_name, + leads.salse_person_id, + leads.contact_person_email, + leads.contact_person_name, + leads.contact_person_mobile, + COALESCE(cp.policy_no, cp_src.policy_no) as policy_no, + COALESCE(cp.policy_start_date, cp_src.policy_start_date) as policy_start_date, + COALESCE(cp.policy_end_date, cp_src.policy_end_date) as policy_end_date, + COALESCE(ins.name, ins_src.name) as insurer_name, + COALESCE(pt.policy_type, pt_src.policy_type) as policy_type, + leads.acm_id, + leads.is_policy_created + ") ->join("leads", "leads.id = lipd.lead_id") ->join("clients ct", "ct.id = leads.client_id", "left") ->join("client_branch cb", "cb.id = leads.client_branch_id", "left") - ->join("client_policy cp", "cp.id = leads.source_policy_id", "left") + ->join("client_policy cp", "cp.id = leads.is_policy_created", "left") + ->join("client_policy cp_src", "cp_src.id = leads.source_policy_id", "left") + ->join("insurers ins", "ins.id = cp.insurer_id", "left") + ->join("insurers ins_src", "ins_src.id = cp_src.insurer_id", "left") + ->join("policy_type pt", "pt.id = cp.policy_type_id", "left") + ->join("policy_type pt_src", "pt_src.id = cp_src.policy_type_id", "left") ->where("lipd.is_active", 1) ->where("lipd.utr_no IS NULL OR lipd.utr_no = ''"); $targetPaymentDate = $this->addBusinessDays($config->installmentReminderBusinessDays); - // print_r($targetPaymentDate);die(); if ($config->installmentReminderFetchOverduePendingUtr) { $builder->groupStart() @@ -178,27 +197,46 @@ class BdsPlacementModel extends Model $data = $builder->get()->getResultArray(); - // print_r($this->db->getLastQuery()->getQuery());die(); - foreach ($data as &$row) { $sales_person_ids = json_decode($row['salse_person_id'], true); $sales_person_id = $sales_person_ids[0] ?? null; + $acm_id = $row['acm_id'] ?? null; + + if ($acm_id) { + $acm_user = $this->db->table('user_profiles') + ->select('email, first_name, last_name') + ->where('id', (int) $acm_id) + ->get() + ->getRowArray(); + + $row['acm_email'] = $acm_user['email'] ?? 'N/A'; + $row['acm_name'] = trim( + ($acm_user['first_name'] ?? '') . ' ' . ($acm_user['last_name'] ?? '') + ) ?: 'Not Assigned'; + } else { + $row['acm_email'] = 'N/A'; + $row['acm_name'] = 'Not Assigned'; + } if ($sales_person_id) { $user = $this->db->table('user_profiles') - ->select('email') + ->select('email, first_name, last_name') ->where('id', (int) $sales_person_id) ->get() ->getRowArray(); $row['sales_person'] = $user['email'] ?? 'N/A'; + $row['sales_person_name'] = trim( + ($user['first_name'] ?? '') . ' ' . ($user['last_name'] ?? '') + ) ?: 'Not Assigned'; } else { $row['sales_person'] = 'Not Assigned'; + $row['sales_person_name'] = 'Not Assigned'; } - $row['heads'] = $heads; - $row['admins'] = $admins; - $row['buisness_team'] = $businessTeam; + // $row['heads'] = $heads; + // $row['admins'] = $admins; + // $row['buisness_team'] = $businessTeam; } return $data; diff --git a/app/Models/PolicyTransactionModel.php b/app/Models/PolicyTransactionModel.php index 2e520918..4f051a66 100644 --- a/app/Models/PolicyTransactionModel.php +++ b/app/Models/PolicyTransactionModel.php @@ -1136,6 +1136,136 @@ return $data; } + public function getCachedInceptionTranctionListData(array $filters): array + { + $cacheKey = 'inception_list_v1_' . md5(json_encode($filters)); + $cache = \Config\Services::cache(); + $cached = $cache->get($cacheKey); + + if (is_array($cached) && isset($cached['rows'], $cached['expires_at'])) { + return $cached; + } + + $ttl = 300; + $rows = $this->getInceptionTranctionListData( + $filters['start_date'] ?? 0, + $filters['end_date'] ?? 0, + $filters['client_id'] ?? 0, + $filters['insurer_id'] ?? 0, + $filters['policy_type_id'] ?? 0, + $filters['date_type'] ?? 0, + $filters['issuer'] ?? 0, + $filters['status'] ?? 0, + $filters['ids'] ?? null + ); + + $payload = [ + 'rows' => $rows, + 'expires_at' => time() + $ttl, + ]; + $cache->save($cacheKey, $payload, $ttl); + + return $payload; + } + + public function filterInceptionTranctionRowsBySearch(array $rows, string $searchValue): array + { + $needle = mb_strtolower(trim($searchValue)); + if ($needle === '') { + return $rows; + } + + $issuerMap = [1 => 'jibs', 2 => 'nhance']; + $issueTypeMap = [1 => 'fresh', 2 => 'renewal', 3 => 'roll over']; + $clientTypeMap = [1 => 'group', 2 => 'individual']; + $statusMap = [ + 'under_process' => 'under process', + 'client_pending' => 'client pending', + 'insurer_pending' => 'insurer pending', + 'co_insurer_pending' => 'co-insurer pending', + 'tpa_pending' => 'tpa pending', + 'validated' => 'validated', + 'cancelled' => 'cancelled', + 'instalment_pending' => 'instalment pending', + 'completed' => 'completed', + 'lost' => 'lost', + ]; + + $fields = [ + 'client_name', + 'client_short_name', + 'client_branch_name', + 'insurer_short_name', + 'policy_type', + 'policy_no', + 'user_name', + 'status', + 'pan', + ]; + + return array_values(array_filter($rows, static function (array $row) use ($needle, $fields, $issuerMap, $issueTypeMap, $clientTypeMap, $statusMap) { + foreach ($fields as $field) { + $value = $row[$field] ?? ''; + if ($value !== '' && mb_strpos(mb_strtolower((string) $value), $needle) !== false) { + return true; + } + } + + // Search by rendered labels shown in list columns. + $issuerText = $issuerMap[(int) ($row['issuer'] ?? 0)] ?? ''; + if ($issuerText !== '' && mb_strpos($issuerText, $needle) !== false) { + return true; + } + + $issueTypeText = $issueTypeMap[(int) ($row['issue_type'] ?? 0)] ?? ''; + if ($issueTypeText !== '' && mb_strpos($issueTypeText, $needle) !== false) { + return true; + } + + $clientTypeText = $clientTypeMap[(int) ($row['client_type'] ?? 0)] ?? ''; + if ($clientTypeText !== '' && mb_strpos($clientTypeText, $needle) !== false) { + return true; + } + + $statusCode = (string) ($row['status'] ?? ''); + $statusText = $statusMap[$statusCode] ?? ''; + if ($statusText !== '' && mb_strpos($statusText, $needle) !== false) { + return true; + } + + return false; + })); + } + + public function getInceptionTranctionListDataTable(int $draw, int $start, int $length, string $searchValue, array $filters): array + { + $cached = $this->getCachedInceptionTranctionListData($filters); + $allRows = $cached['rows'] ?? []; + $recordsTotal = count($allRows); + + if ($searchValue !== '') { + $allRows = $this->filterInceptionTranctionRowsBySearch($allRows, $searchValue); + } + + $recordsFiltered = count($allRows); + + if ($length < 0) { + $length = $recordsFiltered; + } + + $pageRows = $length > 0 + ? array_slice(array_values($allRows), $start, $length) + : array_values($allRows); + + return [ + 'draw' => $draw, + 'recordsTotal' => $recordsTotal, + 'recordsFiltered' => $recordsFiltered, + 'data' => $pageRows, + 'cache_expires_in_ms' => max(0, (($cached['expires_at'] ?? time()) - time()) * 1000), + ]; + } + public function getEndorsementTranctionListData($start_date = 0, $end_date = 0, $client_id = 0, $insurer_id = 0, $policy_type_id = 0, $date_type = 0, $issuer = 0, $status = 0) { @@ -1248,6 +1378,136 @@ return $builder->get()->getResultArray(); } + public function getCachedEndorsementTranctionListData(array $filters): array + { + $cacheKey = 'endorsement_list_v1_' . md5(json_encode($filters)); + $cache = \Config\Services::cache(); + $cached = $cache->get($cacheKey); + + if (is_array($cached) && isset($cached['rows'], $cached['expires_at'])) { + return $cached; + } + + $ttl = 300; + $rows = $this->getEndorsementTranctionListData( + $filters['start_date'] ?? 0, + $filters['end_date'] ?? 0, + $filters['client_id'] ?? 0, + $filters['insurer_id'] ?? 0, + $filters['policy_type_id'] ?? 0, + $filters['date_type'] ?? 0, + $filters['issuer'] ?? 0, + $filters['status'] ?? 0 + ); + + $payload = [ + 'rows' => $rows, + 'expires_at' => time() + $ttl, + ]; + $cache->save($cacheKey, $payload, $ttl); + + return $payload; + } + + public function filterEndorsementTranctionRowsBySearch(array $rows, string $searchValue): array + { + $needle = mb_strtolower(trim($searchValue)); + if ($needle === '') { + return $rows; + } + + $issuerMap = [1 => 'jibs', 2 => 'nhance']; + $statusMap = [ + 'under_process' => 'under process', + 'client_pending' => 'client pending', + 'insurer_pending' => 'insurer pending', + 'co_insurer_pending' => 'co-insurer pending', + 'tpa_pending' => 'tpa pending', + 'validated' => 'validated', + 'cancelled' => 'cancelled', + 'instalment_pending' => 'instalment pending', + 'completed' => 'completed', + ]; + $actionTypeMap = [ + 'addition' => 'addition', + 'deletion' => 'deletion', + 'addition_deletion' => 'addition & deletion', + 'si_enhancement' => 'si enhancement', + 'combo_a_d_si' => 'combo a, d & si', + 'correction' => 'correction', + 'baby_addition' => 'baby addition', + 'policy_instalment' => 'policy instalment', + 'addition_inception' => 'addition-inception', + 'bds_correction' => 'bds correction', + 'policy_correction' => 'policy correction', + 'policy_cancellation' => 'policy cancellation', + ]; + + $fields = [ + 'client_short_name', + 'client_branch_name', + 'insurer_short_name', + 'policy_type', + 'endorsement_no', + 'policy_no', + ]; + + return array_values(array_filter($rows, static function (array $row) use ($needle, $fields, $issuerMap, $statusMap, $actionTypeMap) { + foreach ($fields as $field) { + $value = $row[$field] ?? ''; + if ($value !== '' && mb_strpos(mb_strtolower((string) $value), $needle) !== false) { + return true; + } + } + + $issuerText = $issuerMap[(int) ($row['issuer'] ?? 0)] ?? ''; + if ($issuerText !== '' && mb_strpos($issuerText, $needle) !== false) { + return true; + } + + $statusText = $statusMap[(string) ($row['status'] ?? '')] ?? ''; + if ($statusText !== '' && mb_strpos($statusText, $needle) !== false) { + return true; + } + + $actionTypeText = $actionTypeMap[(string) ($row['action_type'] ?? '')] ?? ''; + if ($actionTypeText !== '' && mb_strpos($actionTypeText, $needle) !== false) { + return true; + } + + return false; + })); + } + + public function getEndorsementTranctionListDataTable(int $draw, int $start, int $length, string $searchValue, array $filters): array + { + $cached = $this->getCachedEndorsementTranctionListData($filters); + $allRows = $cached['rows'] ?? []; + $recordsTotal = count($allRows); + + if ($searchValue !== '') { + $allRows = $this->filterEndorsementTranctionRowsBySearch($allRows, $searchValue); + } + + $recordsFiltered = count($allRows); + + if ($length < 0) { + $length = $recordsFiltered; + } + + $pageRows = $length > 0 + ? array_slice(array_values($allRows), $start, $length) + : array_values($allRows); + + return [ + 'draw' => $draw, + 'recordsTotal' => $recordsTotal, + 'recordsFiltered' => $recordsFiltered, + 'data' => $pageRows, + 'cache_expires_in_ms' => max(0, (($cached['expires_at'] ?? time()) - time()) * 1000), + ]; + } + public function getVarienceReportLIst($start_date = 0, $end_date = 0, $client_id = 0, $insurer_id = 0, $policy_type_id = 0, $date_type = 0, $issuer = 0, $client_branch_id = 0, $insurer_branch_id = 0, $client_policy_id = 0) { $builder = $this->db->table('policy_transaction') @@ -3099,11 +3359,15 @@ } } - // 2. Dynamic $where array + // 2. Dynamic $where (string SQL fragment or column => value map) if (!empty($where)) { - foreach ($where as $column => $value) { - $value = addslashes($value); - $conditions .= " AND `$column` = '$value' "; + if (is_string($where)) { + $conditions .= ' AND ' . $where . ' '; + } else { + foreach ($where as $column => $value) { + $value = addslashes($value); + $conditions .= " AND `$column` = '$value' "; + } } } @@ -3739,119 +4003,245 @@ $query = $this->db->query($sql); $result = $query->getResultArray(); + return $this->processBDSReportResults($result); + } - // $countofalldata = count($result); - // Kint::dump($result); - // dd($this->db->getLastQuery()->getQuery()); - + /** + * Post-process raw BDS report rows (dedup, rewards, unbilled amounts). + */ + public function processBDSReportResults(array $result): array + { $keys = []; $filtered = []; foreach ($result as $row) { - - // Normalize endorsement number $endorsement_number = !empty($row['endorsement_no']) ? $row['endorsement_no'] : '-'; $reward = (float) ($row['reward'] ?? 0); $billed_amt = (float) ($row['billed_amt'] ?? 0); - if($reward == 0.00 && $billed_amt == 0.00 && $row['statement_uploaded'] === 'statement uploaded'){ + if ($reward == 0.00 && $billed_amt == 0.00 && $row['statement_uploaded'] === 'statement uploaded') { continue; } - if ($reward > 0 && ($row['billed_amt'] ?? 0) == 0.00) { - $row['total_irda_amt'] = '0.00'; - $row['billed_amt'] = $reward; - $rewardFlag = 'R'; // Reward row - } else { - $rewardFlag = 'N'; // Normal row + if ($reward > 0 && ($row['billed_amt'] ?? 0) == 0.00) { + $row['total_irda_amt'] = '0.00'; + $row['billed_amt'] = $reward; + $rewardFlag = 'R'; + } else { + $rewardFlag = 'N'; } - // ✅ Stable key (NO reward flag) $key = implode('|', [ $row['statement_year_month'], $row['insurer_name'], $row['policy_no'], $endorsement_number, - $rewardFlag + $rewardFlag, ]); - // Prefer "statement uploaded" if ($row['statement_uploaded'] === 'statement uploaded') { $filtered[$key] = $row; - $keys[$key] = true; - } - // Keep no-statement only if uploaded not present - elseif (!isset($keys[$key])) { + $keys[$key] = true; + } elseif (!isset($keys[$key])) { $filtered[$key] = $row; } } - // Reindex final output $result = array_values($filtered); - // dd($result); - - // -------------------------------------------------------------------------------------------------------- - $totalBilled = []; $totalIrdaMap = []; foreach ($result as $row) { - $key = $row['pt_id'].'-'.$row['insurer_id']; + $key = $row['pt_id'] . '-' . $row['insurer_id']; - // Sum billed amount $totalBilled[$key] = ($totalBilled[$key] ?? 0) + (float) ($row['billed_amt'] ?? 0); - // Store total_irda_amt once if (!isset($totalIrdaMap[$key])) { $totalIrdaMap[$key] = (float) $row['total_irda_amt']; } - if(($row['reward'] ?? 0) > 0){ - $totalIrdaMap[$key] = ($totalIrdaMap[$key] ?? 0) + (float) $row['reward']; + if (($row['reward'] ?? 0) > 0) { + $totalIrdaMap[$key] = ($totalIrdaMap[$key] ?? 0) + (float) $row['reward']; } } - // dd($totalBilled, $totalIrdaMap); - $ptSeen = []; $final = []; foreach ($result as $row) { - $ptId = $row['pt_id'].'-'.$row['insurer_id']; + $ptId = $row['pt_id'] . '-' . $row['insurer_id']; if (!isset($ptSeen[$ptId])) { - - $totalIrdaVal = $totalIrdaMap[$ptId] ?? 0; + $totalIrdaVal = $totalIrdaMap[$ptId] ?? 0; $totalBilledVal = $totalBilled[$ptId] ?? 0; - $addMinus = false; + $addMinus = false; - if($totalIrdaVal < 0){ - $totalIrdaVal = abs($totalIrdaVal); + if ($totalIrdaVal < 0) { + $totalIrdaVal = abs($totalIrdaVal); $totalBilledVal = abs($totalBilledVal); - $addMinus = true; + $addMinus = true; } - // First entry → set unbilled amount - $row['unbilled_amount'] = round((float) ($totalIrdaVal - $totalBilledVal),2 ); + $row['unbilled_amount'] = round((float) ($totalIrdaVal - $totalBilledVal), 2); - if($addMinus){ + if ($addMinus) { $row['unbilled_amount'] = ($row['unbilled_amount'] * -1); } $ptSeen[$ptId] = true; } else { - // Other entries → zero $row['unbilled_amount'] = '0.00'; } $final[] = $row; } - $result = $final; + return $final; + } - // dd($result); die; - return $result; + /** + * Cached processed BDS report list for a given filter set. + */ + public function getCachedBDSReportList(array $filters): array + { + $cacheKey = 'bds_report_v1_' . md5(json_encode($filters)); + $cache = \Config\Services::cache(); + $cached = $cache->get($cacheKey); + + if (is_array($cached)) { + return $cached; + } + + $processed = $this->getBDSReportList( + $filters['start_date'] ?? 0, + $filters['end_date'] ?? 0, + $filters['client_id'] ?? 0, + $filters['insurer_id'] ?? 0, + $filters['policy_type_id'] ?? 0, + $filters['date_type'] ?? 0, + $filters['issuer'] ?? 0, + $filters['client_branch_id'] ?? 0, + $filters['insurer_branch_id'] ?? 0, + $filters['client_policy_id'] ?? 0, + $filters['user_id'] ?? 0, + $filters['where'] ?? [] + ); + + $cache->save($cacheKey, $processed, 300); + + return $processed; + } + + /** + * Server-side DataTables payload for BDS report. + */ + public function getBDSReportListDataTable(int $draw, int $start, int $length, string $searchValue, array $filters): array + { + $allRows = $this->getCachedBDSReportList($filters); + $recordsTotal = count($allRows); + + if ($searchValue !== '') { + $allRows = $this->filterBDSReportRowsBySearch($allRows, $searchValue); + } + + $recordsFiltered = count($allRows); + + if ($length < 0) { + $length = $recordsFiltered; + } + + $pageRows = $length > 0 + ? array_slice(array_values($allRows), $start, $length) + : array_values($allRows); + + return [ + 'draw' => $draw, + 'recordsTotal' => $recordsTotal, + 'recordsFiltered' => $recordsFiltered, + 'data' => $pageRows, + 'totals' => $this->calculateBDSReportTotals($allRows), + ]; + } + + /** + * Global search across BDS report row fields. + */ + public function filterBDSReportRowsBySearch(array $rows, string $searchValue): array + { + $needle = mb_strtolower(trim($searchValue)); + if ($needle === '') { + return $rows; + } + + // Match DataTables "common search" behavior against primary visible columns. + $searchFields = [ + 'user_name', + 'policy_issue_month', + 'client_name', + 'action_type', + 'policy_type', + 'policy_no', + 'endorsement_no', + 'insurer_branch_name', + ]; + + return array_values(array_filter($rows, static function (array $row) use ($needle, $searchFields) { + foreach ($searchFields as $field) { + $value = $row[$field] ?? ''; + if ($value !== '' && mb_strpos(mb_strtolower((string) $value), $needle) !== false) { + return true; + } + } + + $numericFields = [ + 'bp_amt', 'tp_or_ter', 'premium_wo_gst', 'total_premium', 'agreed_bp_per', + 'agreed_tp_or_ter_per', 'reward', 'total_irda_amt', 'billed_amt', 'unbilled_amount', + ]; + foreach ($numericFields as $field) { + $value = $row[$field] ?? ''; + if ($value !== '' && $value !== null && mb_strpos((string) $value, $needle) !== false) { + return true; + } + } + + return false; + })); + } + + /** + * Summary totals for BDS report badges (matches client-side footerCallback logic). + */ + public function calculateBDSReportTotals(array $rows): array + { + $totalPremium = 0.0; + $totalRewards = 0.0; + $totalIrda = 0.0; + $totalBilled = 0.0; + $totalUnbilled = 0.0; + + foreach ($rows as $row) { + $hasIrda = ((float) ($row['total_irda_amt'] ?? 0)) != 0.0; + + $totalPremium += $hasIrda ? (float) ($row['premium_wo_gst'] ?? 0) : 0.0; + $totalRewards += (float) ($row['reward'] ?? 0); + $totalIrda += (float) ($row['total_irda_amt'] ?? 0); + $totalBilled += (float) ($row['billed_amt'] ?? 0); + $totalUnbilled += (float) ($row['unbilled_amount'] ?? 0); + } + + $totalIrdaAmt = $totalBilled - $totalRewards; + $totalRevenue = $totalIrdaAmt + $totalRewards; + + return [ + 'total_premium' => number_format($totalPremium, 2, '.', ''), + 'total_rewards' => number_format($totalRewards, 2, '.', ''), + 'total_irda' => number_format($totalIrdaAmt, 2, '.', ''), + 'total_revenue' => number_format($totalRevenue, 2, '.', ''), + 'total_billed' => number_format($totalBilled, 2, '.', ''), + 'total_unbilled' => number_format($totalUnbilled, 2, '.', ''), + 'policy_count' => count($rows), + ]; } diff --git a/app/Views/bds_installment_reminder_email_template.php b/app/Views/bds_installment_reminder_email_template.php new file mode 100644 index 00000000..edf9a406 --- /dev/null +++ b/app/Views/bds_installment_reminder_email_template.php @@ -0,0 +1,260 @@ + + + + + + + + +
+
+

Policy Installment Payment Reminder

+
Nhance India Insurance
+
+ +
+ +
+ Payment Overdue: The installment for was due on . Kindly arrange the payment at the earliest to keep the policy active. +
+ +
+ Payment Reminder: An installment payment for is due on . Please review the details below. +
+ + +

+ This is a reminder that a policy installment payment is for + ( branch). + Kindly process the payment on or before the due date to ensure uninterrupted policy coverage. +

+ +

+ Payment Details +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Organization
Branch
Policy Number
Policy Type
Insurance Company
Policy Period
Due Date
Amount Payable
Status + + + +
+ +
+

Important Instructions

+
    +
  • Please arrange the installment payment of on or before .
  • +
  • After making the payment, kindly share the payment reference / UTR details for confirmation and record update.
  • +
  • If the payment has already been made, please share the transaction details at the earliest.
  • +
  • For any clarification regarding the amount or due date, please coordinate with the concerned team.
  • +
+
+ +

+ Timely payment helps ensure continuous insurance coverage. Thank you for your cooperation. +

+
+ + +
+ + diff --git a/app/Views/layout/header.php b/app/Views/layout/header.php index 25c51f36..2eed7f7f 100755 --- a/app/Views/layout/header.php +++ b/app/Views/layout/header.php @@ -2150,7 +2150,7 @@ body[data-sidebar-size="condensed"] .footer { - + diff --git a/app/Views/policy_transaction_endorsement_list.php b/app/Views/policy_transaction_endorsement_list.php index 0f2555fa..5dc37652 100644 --- a/app/Views/policy_transaction_endorsement_list.php +++ b/app/Views/policy_transaction_endorsement_list.php @@ -293,40 +293,7 @@ Action  - - $row) { ?> - - - - - - - - - - - - - - - - - - - - + @@ -388,32 +355,122 @@ }) + var endorsementTableFilters = ; + var endorsementListDataTableUrl = ''; + var endorsementClearCacheUrl = ''; + var endorsementAutoReloadTimer = null; + + function scheduleEndorsementAutoReload(ms) { + if (endorsementAutoReloadTimer) { + clearTimeout(endorsementAutoReloadTimer); + } + if (ms > 0) { + endorsementAutoReloadTimer = setTimeout(function() { + window.location.reload(); + }, ms); + } + } + + function exportServerSideFilteredEndorsementData(e, dt, button, config, buttonType) { + var self = this; + var oldStart = dt.page.info().start; + + dt.one('preXhr', function (x, s, data) { + data.start = 0; + data.length = -1; + }); + + dt.one('draw', function () { + $.fn.dataTable.ext.buttons[buttonType].action.call(self, e, dt, button, config); + + dt.one('preXhr', function (x, s, data) { + data.start = oldStart; + data.length = dt.page.len(); + }); + + setTimeout(function () { + dt.ajax.reload(null, false); + }, 0); + }); + + dt.ajax.reload(); + } + + function clearEndorsementCacheAndReloadTable(dt) { + $.ajax({ + url: endorsementClearCacheUrl, + type: 'POST', + success: function(response) { + if (response && response.status) { + endorsementTableFilters.cache_version = new Date().getTime(); + dt.ajax.reload(null, true); + toastr.success(response.message || 'Cache cleared and list reloaded.', 'Success'); + } else { + toastr.warning((response && response.message) ? response.message : 'Unable to clear cache.', 'Warning'); + } + }, + error: function() { + toastr.error('Failed to clear cache.', 'Error'); + } + }); + } + //DataTable document ready $(document).ready(function() { var ticketsTable = $('#tickets-table'); if (ticketsTable.length) { - var table = ticketsTable.DataTable({ - scrollX: true, - dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right - "<'row'<'col-sm-12'tr>>" + - "<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>", - lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]], - buttons: [ + nhanceListDataTableBeforeInit(); + var table = ticketsTable.DataTable(nhanceMergeListDataTableOptions({ + autoWidth: false, + processing: true, + serverSide: true, + deferRender: true, + searchDelay: 400, + columns: (function() { + var cols = []; + for (var i = 0; i < 14; i++) { + cols.push({ data: String(i), orderable: false }); + } + return cols; + })(), + ajax: { + url: endorsementListDataTableUrl, + type: 'POST', + data: function(d) { + return $.extend({}, d, endorsementTableFilters); + }, + dataSrc: function(json) { + scheduleEndorsementAutoReload(json.cache_expires_in_ms || 300000); + return json.data; + } + }, + dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + + "<'row'<'col-sm-12'tr>>" + + "<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>", + lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]], + buttons: [ + { + text: ' Reload ', + className: 'btn app-btn-secondary mr-2', + action: function(e, dt) { + clearEndorsementCacheAndReloadTable(dt); + } + }, { text: ' Filter ', className: 'btn app-btn-primary mr-2', - action: function (e, dt, node, config) { + action: function () { openEndorsementFilterNav(); } }, - { + { text: ' Add ', className: 'btn app-btn-primary mr-2', - action: function (e, dt, node, config) { + action: function () { hide_list_show_add(); addHTMLInput(null, 'policy_docs_div'); - $('#policy_docs').show() + $('#policy_docs').show(); }, attr: { id: 'btnAdd' } }, @@ -422,14 +479,30 @@ text: ' Export ', className: 'btn app-btn-secondary ', buttons: [ - { - extend: 'csv', - text: ' CSV ', - title: 'Policy-Tranction-Endorsement-List', - exportOptions: { - columns: ':not(:last-child)' - }, - } + { + extend: 'csv', + text: ' CSV ', + title: 'Policy-Tranction-Endorsement-List', + action: function (e, dt, button, config) { + exportServerSideFilteredEndorsementData.call(this, e, dt, button, config, 'csvHtml5'); + }, + exportOptions: { + modifier: { search: 'applied', page: 'all' }, + columns: ':not(:last-child)' + }, + }, + { + extend: 'excel', + text: ' EXCEL ', + title: 'Policy-Tranction-Endorsement-List', + action: function (e, dt, button, config) { + exportServerSideFilteredEndorsementData.call(this, e, dt, button, config, 'excelHtml5'); + }, + exportOptions: { + modifier: { search: 'applied', page: 'all' }, + columns: ':not(:last-child)' + }, + } ] } ], @@ -444,33 +517,14 @@ `, searchPlaceholder: "Search", emptyTable: '
No Data found
', - paginate: { - previous: '◄', - next: '►' - } + paginate: { previous: '◄', next: '►' } }, - paging: true, // Enable pagination - pageLength: 10, // Set default number of rows per page (optional) + paging: true, + pageLength: 10, ordering: false, - }); - - function applyBottomRowDropup() { - if (!table) return; - - const currentRows = table.rows({ page: 'current' }).nodes().toArray(); - $('#tickets-table tbody tr').removeClass('nh-force-dropup'); - $('#tickets-table tbody tr td.table-action-cell .btn-group.dropdown').removeClass('dropup'); - - const targetCount = Math.min(2, currentRows.length); - for (let i = 0; i < targetCount; i++) { - const row = currentRows[currentRows.length - 1 - i]; - if (!row) continue; - $(row).addClass('nh-force-dropup'); - $(row).find('td.table-action-cell .btn-group.dropdown').addClass('dropup'); - } - } - applyBottomRowDropup(); - ticketsTable.on('draw.dt', applyBottomRowDropup); + })); + nhanceListDataTableAfterInit(); + nhanceListDataTableBindAdjust(table); } else { console.error("Table not found."); } diff --git a/app/Views/policy_transaction_inception_list.php b/app/Views/policy_transaction_inception_list.php index 9a48f2d2..a86effe9 100644 --- a/app/Views/policy_transaction_inception_list.php +++ b/app/Views/policy_transaction_inception_list.php @@ -321,43 +321,7 @@ table.dataTable tbody td { - - $row){ ?> - - - - - - - - - - - - - - - - - - - - - - + @@ -673,6 +637,66 @@ function getAddPage(){ } } +var inceptionTableFilters = ; +var inceptionListDataTableUrl = ''; +var inceptionClearCacheUrl = ''; +var inceptionAutoReloadTimer = null; + +function scheduleInceptionAutoReload(ms) { + if (inceptionAutoReloadTimer) { + clearTimeout(inceptionAutoReloadTimer); + } + if (ms > 0) { + inceptionAutoReloadTimer = setTimeout(function() { + window.location.reload(); + }, ms); + } +} + +function exportServerSideFilteredInceptionData(e, dt, button, config, buttonType) { + var self = this; + var oldStart = dt.page.info().start; + + dt.one('preXhr', function (x, s, data) { + data.start = 0; + data.length = -1; + }); + + dt.one('draw', function () { + $.fn.dataTable.ext.buttons[buttonType].action.call(self, e, dt, button, config); + + dt.one('preXhr', function (x, s, data) { + data.start = oldStart; + data.length = dt.page.len(); + }); + + setTimeout(function () { + dt.ajax.reload(null, false); + }, 0); + }); + + dt.ajax.reload(); +} + +function clearInceptionCacheAndReloadTable(dt) { + $.ajax({ + url: inceptionClearCacheUrl, + type: 'POST', + success: function(response) { + if (response && response.status) { + inceptionTableFilters.cache_version = new Date().getTime(); + dt.ajax.reload(null, true); + toastr.success(response.message || 'Cache cleared and list reloaded.', 'Success'); + } else { + toastr.warning((response && response.message) ? response.message : 'Unable to clear cache.', 'Warning'); + } + }, + error: function() { + toastr.error('Failed to clear cache.', 'Error'); + } + }); +} + // Datatable document ready $(document).ready(function() { @@ -692,64 +716,109 @@ $(document).ready(function() { var ticketsTable = $('#tickets-table'); if (ticketsTable.length) { - ticketsTable.DataTable({ - scrollX: true, - // dom: "<'row'<'col-12'f><'col-sm-11 text-right'B>>" + // Filter left, buttons right - // dom: "<'row'<'col-12 d-flex justify-content-between'<'datatable-search dataTables_filter'f><'datatable-buttons dt-buttons'B>>>" + - // "<'row'<'col-sm-12'tr>>" + - // "<'row'<'col-sm-5'i><'col-sm-7'p>>", // Pagination at the bottom, without page length selector - dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + // Filter left, buttons right + nhanceListDataTableBeforeInit(); + var inceptionTable = ticketsTable.DataTable(nhanceMergeListDataTableOptions({ + autoWidth: false, + processing: true, + serverSide: true, + deferRender: true, + searchDelay: 400, + columns: (function() { + var cols = []; + for (var i = 0; i < 16; i++) { + cols.push({ data: String(i), orderable: false }); + } + return cols; + })(), + ajax: { + url: inceptionListDataTableUrl, + type: 'POST', + data: function(d) { + return $.extend({}, d, inceptionTableFilters); + }, + dataSrc: function(json) { + scheduleInceptionAutoReload(json.cache_expires_in_ms || 300000); + return json.data; + } + }, + dom: "<'row'<'col-sm-7'f><'col-sm-5 text-right'B>>" + "<'row'<'col-sm-12'tr>>" + "<'row align-items-center'<'col-5 text-start'i><'col-7 d-flex justify-content-end align-items-center'<'me-2'l>p>>", lengthMenu: [[10, 20, 50, 100], [10, 20, 50, 100]], - buttons: [ - { - text: ' Filter ', - className: 'btn app-btn-primary mr-2', - action: function(e, dt, node, config) { - openPolicyFilterNav(); - } - }, - { - text: ' Add ', - className: 'btn app-btn-primary mr-2', - action: function (e, dt, node, config) { - openPolicyNoAddModal(); - } - }, - { - extend: 'collection', - text: ' Export ', - className: 'btn app-btn-secondary ', - buttons: [ - { - extend: 'csv', - text: ' CSV ', - className: 'app-btn-primary ', - title: 'Policy-Tranction-Inception-List', - exportOptions: { - columns: ':not(:last-child)' - }, - } - ] + buttons: [ + { + text: ' Reload ', + className: 'btn app-btn-secondary mr-2', + action: function(e, dt) { + clearInceptionCacheAndReloadTable(dt); } - ], + }, + { + text: ' Filter ', + className: 'btn app-btn-primary mr-2', + action: function() { + openPolicyFilterNav(); + } + }, + { + text: ' Add ', + className: 'btn app-btn-primary mr-2', + action: function () { + openPolicyNoAddModal(); + } + }, + { + extend: 'collection', + text: ' Export ', + className: 'btn app-btn-secondary ', + buttons: [ + { + extend: 'csv', + text: ' CSV ', + className: 'app-btn-primary ', + title: 'Policy-Tranction-Inception-List', + action: function (e, dt, button, config) { + exportServerSideFilteredInceptionData.call(this, e, dt, button, config, 'csvHtml5'); + }, + exportOptions: { + modifier: { search: 'applied', page: 'all' }, + columns: ':not(:last-child)' + } + }, + { + extend: 'excel', + text: ' EXCEL ', + className: 'app-btn-primary ', + title: 'Policy-Tranction-Inception-List', + action: function (e, dt, button, config) { + exportServerSideFilteredInceptionData.call(this, e, dt, button, config, 'excelHtml5'); + }, + exportOptions: { + modifier: { search: 'applied', page: 'all' }, + columns: ':not(:last-child)' + } + } + ] + } + ], language: { - search: ` -
- _INPUT_ - - -
`, - searchPlaceholder: "Search", - emptyTable: '
No Data found
' + search: ` +
+ _INPUT_ + + +
`, + searchPlaceholder: "Search", + emptyTable: '
No Data found
' }, - paging: true, // Enable pagination - pageLength: 10, // Set default number of rows per page (optional) - // ordering: false, - }); + paging: true, + pageLength: 10, + ordering: false, + })); + nhanceListDataTableAfterInit(); + nhanceListDataTableBindAdjust(inceptionTable); } else { console.error("Table not found."); } diff --git a/app/Views/report_bds.php b/app/Views/report_bds.php index 80246a6d..fb2c1c90 100644 --- a/app/Views/report_bds.php +++ b/app/Views/report_bds.php @@ -165,130 +165,6 @@ - - $row){ ?> - -   " class="mdi mdi-pencil" > - - - - - - - - - - - - - - - - - - - - - - - - - - % - % - - - i am print 0.00. - // REF : Velmurugan but he told handle in query - // Date : 6/11/25 12:50 - $irda_amt = isset($row['total_irda_amt']) && $row['total_irda_amt'] !== '' ? (float)$row['total_irda_amt'] : 0; - $exp_amt = isset($row['exp_amt']) && $row['exp_amt'] !== '' ? (float)$row['exp_amt'] : 0; - // $max_amount = max($irda_amt, $exp_amt); - // $total_irda_amt = number_format($max_amount, 2, '.', ''); - // $total_irda_amt = empty($row['total_irda_amt']) ? $row['exp_amt'] : $row['total_irda_amt']; - $total_irda_amt = $row['total_irda_amt'] ?? '0.00'; - ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -332,6 +208,68 @@