742 lines
24 KiB
PHP
742 lines
24 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Exports\InvoicesExport;
|
|
use App\Http\Requests\LambdaRequest;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Maatwebsite\Excel\Facades\Excel;
|
|
|
|
/**
|
|
* Titans HCP Gathering Report — MySQL-backed import + token report.
|
|
*
|
|
* Quick Action (Rapport admin, titans partition):
|
|
* Type: external_with_token
|
|
* URL: https://<laravel-domain>/titanshcpgatheringreport/report?token=TOKEN
|
|
*/
|
|
class TitanshcpgatheringreportController extends Controller
|
|
{
|
|
private const DOMAIN = 'titans';
|
|
|
|
private const SESSION_TOKEN_KEY = 'titans_hcp_gathering_report_token';
|
|
|
|
private ?string $accessToken = null;
|
|
|
|
private ?object $reportUser = null;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->_viewname = 'titanshcpgatheringreport';
|
|
$this->_title = 'Titans HCP Gathering Report';
|
|
$this->_base_url = '/titanshcpgatheringreport';
|
|
|
|
$this->middleware(function ($request, $next) {
|
|
$action = $request->route('action');
|
|
if (in_array($action, ['report', 'export'], true)) {
|
|
return $next($request);
|
|
}
|
|
if ($action === null && $request->route()->getName() === 'titanshcpgatheringreport-list' && $request->filled('token')) {
|
|
return $next($request);
|
|
}
|
|
if (!Auth::check()) {
|
|
return redirect('/login');
|
|
}
|
|
return $next($request);
|
|
});
|
|
}
|
|
|
|
public function list(Request $request)
|
|
{
|
|
if ($request->filled('token')) {
|
|
return redirect($this->buildUrl('/report', $request));
|
|
}
|
|
|
|
$this->assertTitansAdmin();
|
|
|
|
$imports = DB::table('titans_hcp_gathering_imports')
|
|
->orderByDesc('created_at')
|
|
->limit(100)
|
|
->get();
|
|
|
|
return $this->view('list')->with([
|
|
'title' => $this->_title,
|
|
'base_url' => $this->_base_url,
|
|
'imports' => $imports,
|
|
'buttons' => $this->getListButtons(),
|
|
]);
|
|
}
|
|
|
|
public function import(Request $request)
|
|
{
|
|
$this->assertTitansAdmin();
|
|
|
|
return $this->view('import')->with([
|
|
'title' => 'Import Titans HCP Data',
|
|
'base_url' => $this->_base_url,
|
|
]);
|
|
}
|
|
|
|
public function postimport(Request $request)
|
|
{
|
|
$this->assertTitansAdmin();
|
|
|
|
try {
|
|
$validator = Validator::make($request->all(), [
|
|
'report_month' => 'required|date_format:Y-m',
|
|
'import_file' => 'required|file|max:10240',
|
|
], [
|
|
'report_month.required' => 'Please select a report month.',
|
|
'report_month.date_format' => 'Report month must be in YYYY-MM format.',
|
|
'import_file.required' => 'Please choose a file to upload.',
|
|
'import_file.file' => 'The upload did not contain a valid file.',
|
|
'import_file.max' => 'The file may not be larger than 10MB.',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return redirect()->back()
|
|
->withErrors($validator)
|
|
->withInput()
|
|
->with('error', $validator->errors()->first());
|
|
}
|
|
|
|
$file = $request->file('import_file');
|
|
$extension = strtolower((string) $file->getClientOriginalExtension());
|
|
if (!in_array($extension, ['xlsx', 'xls', 'csv'], true)) {
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', 'Only .xlsx, .xls, and .csv files are supported.');
|
|
}
|
|
|
|
$reportMonth = $request->input('report_month');
|
|
$importId = DB::table('titans_hcp_gathering_imports')->insertGetId([
|
|
'file_name' => $file->getClientOriginalName(),
|
|
'report_month' => $reportMonth,
|
|
'status' => 'processing',
|
|
'uploaded_by' => (string) Auth::id(),
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
$inserted = 0;
|
|
$replaced = 0;
|
|
$skipped = 0;
|
|
$errors = [];
|
|
|
|
$rows = $this->parseImportFile($file);
|
|
if (empty($rows['headers']) || empty($rows['data'])) {
|
|
throw new \RuntimeException('The file is empty or has no data rows.');
|
|
}
|
|
|
|
$columnIndex = $this->mapHeaderIndexes($rows['headers']);
|
|
if (!isset($columnIndex['pcode']) || !isset($columnIndex['legacy_code'])) {
|
|
throw new \RuntimeException('Required columns PCODE and LEGACY_CODE were not found in the file header row.');
|
|
}
|
|
|
|
foreach ($rows['data'] as $rowIndex => $row) {
|
|
$lineNo = $rowIndex + 2;
|
|
try {
|
|
$record = $this->mapRowToRecord($row, $columnIndex);
|
|
if (empty($record['pcode']) || empty($record['legacy_code'])) {
|
|
$skipped++;
|
|
continue;
|
|
}
|
|
|
|
$existing = DB::table('titans_hcp_gathering')
|
|
->where('pcode', $record['pcode'])
|
|
->where('legacy_code', $record['legacy_code'])
|
|
->where('report_month', $reportMonth)
|
|
->first();
|
|
|
|
$payload = array_merge($record, [
|
|
'report_month' => $reportMonth,
|
|
'import_id' => $importId,
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
if ($existing) {
|
|
DB::table('titans_hcp_gathering')->where('id', $existing->id)->update($payload);
|
|
$replaced++;
|
|
} else {
|
|
DB::table('titans_hcp_gathering')->insert(array_merge($payload, [
|
|
'created_at' => now(),
|
|
]));
|
|
$inserted++;
|
|
}
|
|
} catch (\Throwable $e) {
|
|
$errors[] = $this->formatRowError($lineNo, $e);
|
|
}
|
|
}
|
|
|
|
$status = empty($errors) ? 'completed' : 'completed_with_errors';
|
|
$this->updateImportLog($importId, [
|
|
'status' => $status,
|
|
'total_rows' => count($rows['data']),
|
|
'inserted_count' => $inserted,
|
|
'replaced_count' => $replaced,
|
|
'skipped_count' => $skipped,
|
|
'error_count' => count($errors),
|
|
'error_log' => empty($errors) ? null : implode("\n", array_slice($errors, 0, 50)),
|
|
'processed_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
$message = "Import complete. Inserted: {$inserted}, Replaced: {$replaced}, Skipped: {$skipped}.";
|
|
if (!empty($errors)) {
|
|
$message .= ' Errors: ' . count($errors) . ' (see import log).';
|
|
}
|
|
if ($inserted === 0 && $replaced === 0) {
|
|
$message .= ' No rows were imported. Check that PCODE and LEGACY_CODE columns are present and populated.';
|
|
}
|
|
|
|
return redirect('/titanshcpgatheringreports')->with('message', $message);
|
|
} catch (\Throwable $e) {
|
|
Log::error('Titans HCP gathering import failed', [
|
|
'error' => $e->getMessage(),
|
|
'user_id' => Auth::id(),
|
|
]);
|
|
|
|
if (isset($importId)) {
|
|
$this->updateImportLog($importId, [
|
|
'status' => 'failed',
|
|
'error_log' => $e->getMessage(),
|
|
'processed_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
}
|
|
|
|
return redirect()->back()
|
|
->withInput()
|
|
->with('error', $this->sanitizeDbString($e->getMessage()) ?: 'Import failed.');
|
|
}
|
|
}
|
|
|
|
public function report(Request $request)
|
|
{
|
|
$token = $this->assertTitansAccess($request);
|
|
$month = $this->resolveReportMonth($request);
|
|
$months = $this->availableMonths();
|
|
$rows = $this->fetchReportRows($month);
|
|
$tmVisitLabel = $this->tmVisitColumnLabel($month);
|
|
|
|
return $this->view('report')->with([
|
|
'title' => $this->_title,
|
|
'base_url' => $this->_base_url,
|
|
'token' => $token,
|
|
'month' => $month,
|
|
'months' => $months,
|
|
'rows' => $rows,
|
|
'tm_visit_label' => $tmVisitLabel,
|
|
]);
|
|
}
|
|
|
|
public function export(Request $request)
|
|
{
|
|
$this->assertTitansAccess($request);
|
|
$month = $this->resolveReportMonth($request);
|
|
$rows = $this->fetchReportRows($month);
|
|
$tmVisitLabel = $this->tmVisitColumnLabel($month);
|
|
|
|
$header = [
|
|
'ZBM', 'RBM', 'AREA', 'HQ', 'TM Name', 'HCP NAME',
|
|
'LEGACY_CODE', 'PCODE', 'AgreementNo', 'Status', 'EAS ID',
|
|
'Activity Report Status', 'Payment Status', 'Closure Done', 'Closure Approved',
|
|
$tmVisitLabel,
|
|
];
|
|
|
|
$excelData = [$header];
|
|
foreach ($rows as $row) {
|
|
$excelData[] = [
|
|
$row->zbm,
|
|
$row->rbm,
|
|
$row->area,
|
|
$row->hq,
|
|
$row->tm_name,
|
|
$row->hcp_name,
|
|
$row->legacy_code,
|
|
$row->pcode,
|
|
$row->agreement_no,
|
|
$row->status,
|
|
$row->eas_id,
|
|
$row->activity_report_status,
|
|
$row->payment_status,
|
|
$row->closure_done,
|
|
$row->closure_approved,
|
|
$row->tm_visit,
|
|
];
|
|
}
|
|
|
|
$filename = 'titans-hcp-gathering-' . $month . '.xlsx';
|
|
return Excel::download(new InvoicesExport($excelData), $filename);
|
|
}
|
|
|
|
protected function getListButtons(): array
|
|
{
|
|
return [
|
|
['title' => 'Import', 'link' => $this->_base_url . '/import'],
|
|
];
|
|
}
|
|
|
|
protected function assertTitansAdmin(): void
|
|
{
|
|
$user = Auth::user();
|
|
$permissions = json_decode($user->permissions ?? '[]');
|
|
if (!is_array($permissions)) {
|
|
$permissions = [];
|
|
}
|
|
if (($user->domain ?? '') !== self::DOMAIN) {
|
|
abort(403);
|
|
}
|
|
if (!in_array('all_data_access', $permissions, true) && !in_array('formexports_get_all', $permissions, true)) {
|
|
abort(403);
|
|
}
|
|
}
|
|
|
|
protected function assertTitansAccess(Request $request): string
|
|
{
|
|
$token = $this->resolveAccessToken($request);
|
|
if (!$token) {
|
|
abort(401, 'Access token is required');
|
|
}
|
|
|
|
$user = $this->getUserFromToken($token);
|
|
if (!$user || !$this->isTitansUser($user)) {
|
|
abort(403, 'Invalid access token or user is not in the Titans domain.');
|
|
}
|
|
|
|
$this->accessToken = $token;
|
|
$this->reportUser = $user;
|
|
return $token;
|
|
}
|
|
|
|
protected function resolveAccessToken(Request $request): ?string
|
|
{
|
|
$fromRequest = $this->extractRequestToken($request);
|
|
if ($fromRequest !== '') {
|
|
$user = $this->getUserFromToken($fromRequest);
|
|
if ($user && $this->isTitansUser($user)) {
|
|
$this->persistAccessToken($request, $fromRequest);
|
|
|
|
return $fromRequest;
|
|
}
|
|
}
|
|
|
|
$storedToken = $this->loadPersistedToken($request);
|
|
if ($storedToken !== '') {
|
|
$user = $this->getUserFromToken($storedToken);
|
|
if ($user && $this->isTitansUser($user)) {
|
|
return $storedToken;
|
|
}
|
|
|
|
$this->forgetPersistedToken($request);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function extractRequestToken(Request $request): string
|
|
{
|
|
$raw = $request->query('token', $request->input('token'));
|
|
if ($raw === null || $raw === '') {
|
|
$raw = $request->header('Authorization') ?? '';
|
|
}
|
|
|
|
return $this->normalizeRawToken((string) $raw);
|
|
}
|
|
|
|
protected function persistAccessToken(Request $request, string $token): void
|
|
{
|
|
$request->session()->put(self::SESSION_TOKEN_KEY, $token);
|
|
}
|
|
|
|
protected function loadPersistedToken(Request $request): string
|
|
{
|
|
if (!$request->session()->has(self::SESSION_TOKEN_KEY)) {
|
|
return '';
|
|
}
|
|
|
|
return $this->normalizeRawToken((string) $request->session()->get(self::SESSION_TOKEN_KEY));
|
|
}
|
|
|
|
protected function forgetPersistedToken(Request $request): void
|
|
{
|
|
$request->session()->forget(self::SESSION_TOKEN_KEY);
|
|
}
|
|
|
|
protected function normalizeRawToken(string $token): string
|
|
{
|
|
$token = trim($token);
|
|
if (stripos($token, 'Bearer ') === 0) {
|
|
$token = trim(substr($token, 7));
|
|
}
|
|
|
|
return $token;
|
|
}
|
|
|
|
protected function getUserFromToken(string $token): ?object
|
|
{
|
|
$payload = $this->decodeTokenPayload($token);
|
|
if (!is_object($payload)) {
|
|
return null;
|
|
}
|
|
|
|
$user = $payload->user ?? null;
|
|
if (!is_object($user)) {
|
|
return null;
|
|
}
|
|
|
|
if (!isset($user->permissions) && isset($payload->permissions)) {
|
|
$user->permissions = $payload->permissions;
|
|
}
|
|
if (!isset($user->all_data_access) && isset($payload->all_data_access)) {
|
|
$user->all_data_access = $payload->all_data_access;
|
|
}
|
|
|
|
return $user;
|
|
}
|
|
|
|
protected function decodeTokenPayload(string $token): ?object
|
|
{
|
|
$token = $this->normalizeRawToken($token);
|
|
$parts = explode('.', $token);
|
|
if (count($parts) < 2 || $parts[1] === '') {
|
|
return null;
|
|
}
|
|
|
|
$payloadSegment = strtr($parts[1], '-_', '+/');
|
|
$padding = strlen($payloadSegment) % 4;
|
|
if ($padding > 0) {
|
|
$payloadSegment .= str_repeat('=', 4 - $padding);
|
|
}
|
|
|
|
$payload = json_decode(base64_decode($payloadSegment));
|
|
|
|
return is_object($payload) ? $payload : null;
|
|
}
|
|
|
|
protected function isTitansUser(object $user): bool
|
|
{
|
|
$domain = strtolower((string) ($user->domain ?? ''));
|
|
if ($domain === self::DOMAIN) {
|
|
return true;
|
|
}
|
|
|
|
$partition = strtolower((string) ($user->partition ?? ''));
|
|
return $partition === 'rapport-' . self::DOMAIN;
|
|
}
|
|
|
|
protected function buildUrl(string $path, Request $request): string
|
|
{
|
|
$query = array_filter([
|
|
'token' => $request->input('token') ?: $request->session()->get(self::SESSION_TOKEN_KEY),
|
|
'month' => $request->input('month'),
|
|
]);
|
|
$url = $this->_base_url . $path;
|
|
if (!empty($query)) {
|
|
$url .= '?' . http_build_query($query);
|
|
}
|
|
return $url;
|
|
}
|
|
|
|
protected function resolveReportMonth(Request $request): string
|
|
{
|
|
$month = $request->input('month');
|
|
if ($month && preg_match('/^\d{4}-\d{2}$/', $month)) {
|
|
return $month;
|
|
}
|
|
$latest = DB::table('titans_hcp_gathering')->max('report_month');
|
|
return $latest ?: Carbon::now()->format('Y-m');
|
|
}
|
|
|
|
protected function availableMonths(): array
|
|
{
|
|
$query = DB::table('titans_hcp_gathering')
|
|
->select('report_month')
|
|
->distinct();
|
|
$query = $this->applyReportUserScope($query, $this->reportUser);
|
|
|
|
return $query
|
|
->orderByDesc('report_month')
|
|
->pluck('report_month')
|
|
->all();
|
|
}
|
|
|
|
protected function fetchReportRows(string $month)
|
|
{
|
|
$query = DB::table('titans_hcp_gathering')
|
|
->where('report_month', $month);
|
|
$query = $this->applyReportUserScope($query, $this->reportUser);
|
|
|
|
return $query
|
|
->orderBy('hcp_name')
|
|
->orderBy('pcode')
|
|
->get();
|
|
}
|
|
|
|
protected function userHasAllDataAccess(?object $user): bool
|
|
{
|
|
if (!$user) {
|
|
return false;
|
|
}
|
|
|
|
if (!empty($user->all_data_access)) {
|
|
return true;
|
|
}
|
|
|
|
$permissions = $user->permissions ?? '[]';
|
|
if (is_string($permissions)) {
|
|
$permissions = json_decode($permissions, true);
|
|
}
|
|
if (!is_array($permissions)) {
|
|
$permissions = [];
|
|
}
|
|
|
|
return in_array('all_data_access', $permissions, true);
|
|
}
|
|
|
|
protected function resolveScopedHierarchyIds(?object $user): array
|
|
{
|
|
if (!$user || empty($user->id)) {
|
|
return [];
|
|
}
|
|
|
|
$partition = 'rapport-' . ($user->domain ?? self::DOMAIN);
|
|
$hierarchyIds = [];
|
|
|
|
if (!empty($user->hierarchy)) {
|
|
$hierarchyIds[] = $user->hierarchy;
|
|
}
|
|
|
|
try {
|
|
$response = LambdaRequest::getrequest('ums', 'hierarchy/hierarchy_below_me', [
|
|
'partition' => $partition,
|
|
'user_id' => $user->id,
|
|
]);
|
|
if (isset($response->data->hierarchies) && is_array($response->data->hierarchies)) {
|
|
foreach ($response->data->hierarchies as $hierarchy) {
|
|
if (!empty($hierarchy->id)) {
|
|
$hierarchyIds[] = $hierarchy->id;
|
|
}
|
|
}
|
|
}
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Titans HCP report hierarchy scope failed', [
|
|
'user_id' => $user->id ?? null,
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
|
|
return array_values(array_unique(array_filter($hierarchyIds)));
|
|
}
|
|
|
|
protected function applyReportUserScope($query, ?object $user)
|
|
{
|
|
if (!$user || $this->userHasAllDataAccess($user)) {
|
|
return $query;
|
|
}
|
|
|
|
$hierarchyIds = $this->resolveScopedHierarchyIds($user);
|
|
$userId = (string) ($user->id ?? '');
|
|
|
|
return $query->where(function ($scopedQuery) use ($hierarchyIds, $userId) {
|
|
if (empty($hierarchyIds) && $userId === '') {
|
|
$scopedQuery->whereRaw('1 = 0');
|
|
return;
|
|
}
|
|
|
|
if (!empty($hierarchyIds)) {
|
|
$scopedQuery->whereIn('hierarchy_id', $hierarchyIds);
|
|
}
|
|
if ($userId !== '') {
|
|
$scopedQuery->orWhere('user_id', $userId);
|
|
}
|
|
});
|
|
}
|
|
|
|
protected function tmVisitColumnLabel(string $month): string
|
|
{
|
|
try {
|
|
return Carbon::createFromFormat('Y-m', $month)->format('F') . ' TM Visit';
|
|
} catch (\Throwable $e) {
|
|
return 'TM Visit';
|
|
}
|
|
}
|
|
|
|
protected function parseImportFile($file): array
|
|
{
|
|
$extension = strtolower($file->getClientOriginalExtension());
|
|
if (in_array($extension, ['xlsx', 'xls'], true)) {
|
|
$data = Excel::toArray([], $file);
|
|
if (empty($data[0])) {
|
|
return ['headers' => [], 'data' => []];
|
|
}
|
|
$sheet = $data[0];
|
|
$headers = array_shift($sheet);
|
|
return ['headers' => $headers, 'data' => $sheet];
|
|
}
|
|
|
|
$lines = array_map('str_getcsv', file($file->getRealPath()));
|
|
$headers = array_shift($lines) ?: [];
|
|
return ['headers' => $headers, 'data' => $lines];
|
|
}
|
|
|
|
protected function normalizeHeader(string $header): string
|
|
{
|
|
$header = preg_replace('/^\xEF\xBB\xBF/', '', $header);
|
|
$header = strtolower(trim(preg_replace('/\s+/', ' ', $header)));
|
|
return $header;
|
|
}
|
|
|
|
protected function mapHeaderIndexes(array $headers): array
|
|
{
|
|
$map = [];
|
|
$tmVisitIndex = null;
|
|
foreach ($headers as $index => $header) {
|
|
$normalized = $this->normalizeHeader((string) $header);
|
|
if (str_contains($normalized, 'tm visit')) {
|
|
$tmVisitIndex = $index;
|
|
continue;
|
|
}
|
|
$field = $this->headerToField($normalized);
|
|
if ($field) {
|
|
$map[$field] = $index;
|
|
}
|
|
}
|
|
if ($tmVisitIndex !== null) {
|
|
$map['tm_visit'] = $tmVisitIndex;
|
|
}
|
|
return $map;
|
|
}
|
|
|
|
protected function headerToField(string $normalized): ?string
|
|
{
|
|
$aliases = [
|
|
's.no' => 'sno',
|
|
'sno' => 'sno',
|
|
's no' => 'sno',
|
|
'zbm' => 'zbm',
|
|
'rbm' => 'rbm',
|
|
'area' => 'area',
|
|
'hq' => 'hq',
|
|
'tm name' => 'tm_name',
|
|
'hcp name' => 'hcp_name',
|
|
'doctor_id' => 'doctor_id',
|
|
'doctor id' => 'doctor_id',
|
|
'hierarchy_id' => 'hierarchy_id',
|
|
'hierarchy id' => 'hierarchy_id',
|
|
'user_id' => 'user_id',
|
|
'user id' => 'user_id',
|
|
'legacy_code' => 'legacy_code',
|
|
'legacy code' => 'legacy_code',
|
|
'pcode' => 'pcode',
|
|
'agreementno' => 'agreement_no',
|
|
'agreement no' => 'agreement_no',
|
|
'status' => 'status',
|
|
'eas id' => 'eas_id',
|
|
'activity report status' => 'activity_report_status',
|
|
'payment status' => 'payment_status',
|
|
'closure done' => 'closure_done',
|
|
'closure approved' => 'closure_approved',
|
|
];
|
|
return $aliases[$normalized] ?? null;
|
|
}
|
|
|
|
protected function mapRowToRecord(array $row, array $columnIndex): array
|
|
{
|
|
$get = function (string $field) use ($row, $columnIndex) {
|
|
if (!isset($columnIndex[$field])) {
|
|
return null;
|
|
}
|
|
$value = $row[$columnIndex[$field]] ?? null;
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
return $this->sanitizeDbString((string) $value);
|
|
};
|
|
|
|
$sno = $get('sno');
|
|
return [
|
|
'sno' => is_numeric($sno) ? (int) $sno : null,
|
|
'zbm' => $get('zbm'),
|
|
'rbm' => $get('rbm'),
|
|
'area' => $get('area'),
|
|
'hq' => $get('hq'),
|
|
'tm_name' => $get('tm_name'),
|
|
'hcp_name' => $get('hcp_name'),
|
|
'doctor_id' => $get('doctor_id'),
|
|
'hierarchy_id' => $get('hierarchy_id'),
|
|
'user_id' => $get('user_id'),
|
|
'legacy_code' => $get('legacy_code'),
|
|
'pcode' => $get('pcode'),
|
|
'agreement_no' => $get('agreement_no'),
|
|
'status' => $get('status'),
|
|
'eas_id' => $get('eas_id'),
|
|
'activity_report_status' => $get('activity_report_status'),
|
|
'payment_status' => $get('payment_status'),
|
|
'closure_done' => $get('closure_done'),
|
|
'closure_approved' => $get('closure_approved'),
|
|
'tm_visit' => $get('tm_visit'),
|
|
];
|
|
}
|
|
|
|
protected function sanitizeDbString(?string $value): ?string
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
$value = str_replace("\0", '', $value);
|
|
|
|
if (!mb_check_encoding($value, 'UTF-8')) {
|
|
$converted = @iconv('Windows-1252', 'UTF-8//IGNORE', $value);
|
|
if ($converted !== false && $converted !== '') {
|
|
$value = $converted;
|
|
}
|
|
}
|
|
|
|
if (function_exists('iconv')) {
|
|
$converted = @iconv('UTF-8', 'UTF-8//IGNORE', $value);
|
|
if ($converted !== false) {
|
|
$value = $converted;
|
|
}
|
|
} elseif (function_exists('mb_convert_encoding')) {
|
|
$value = mb_convert_encoding($value, 'UTF-8', 'UTF-8');
|
|
}
|
|
|
|
$value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $value) ?? '';
|
|
$value = trim($value);
|
|
|
|
return $value === '' ? null : $value;
|
|
}
|
|
|
|
protected function formatRowError(int $lineNo, \Throwable $e): string
|
|
{
|
|
$message = $e->getMessage();
|
|
if (str_contains($message, 'Incorrect string value')) {
|
|
return "Row {$lineNo}: Invalid characters in row data.";
|
|
}
|
|
|
|
$message = $this->sanitizeDbString($message) ?: 'Unknown row error.';
|
|
if (strlen($message) > 240) {
|
|
$message = substr($message, 0, 240) . '...';
|
|
}
|
|
|
|
return "Row {$lineNo}: {$message}";
|
|
}
|
|
|
|
protected function updateImportLog(int $importId, array $payload): void
|
|
{
|
|
if (array_key_exists('error_log', $payload)) {
|
|
$payload['error_log'] = $payload['error_log'] === null
|
|
? null
|
|
: $this->sanitizeDbString((string) $payload['error_log']);
|
|
}
|
|
|
|
DB::table('titans_hcp_gathering_imports')->where('id', $importId)->update($payload);
|
|
}
|
|
}
|