469 lines
17 KiB
PHP
469 lines
17 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use App\Http\Requests\LambdaRequest;
|
|
use App\Services\NeblogisticAuditService;
|
|
use Log;
|
|
|
|
class Neblogistics extends Command
|
|
{
|
|
/**
|
|
* The name and signature of the console command.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $signature = 'neblogistics:sync';
|
|
|
|
/**
|
|
* The console command description.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $description = 'Sync neblogistic TMS mappings and repair null status rows from form submissions';
|
|
|
|
/**
|
|
* Create a new command instance.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* Execute the console command.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
|
|
public function handle_new()
|
|
{
|
|
$records = DB::table('neblogistic')
|
|
->where('request_letter', 'like', '%https://d1zmfdp4rdvnc4.cloudfront.net/d1zmfdp4rdvnc4.cloudfront.net%')
|
|
->get();
|
|
|
|
foreach ($records as $record) {
|
|
$updated = str_replace(
|
|
'https://d1zmfdp4rdvnc4.cloudfront.net/d1zmfdp4rdvnc4.cloudfront.net/',
|
|
'https://d1zmfdp4rdvnc4.cloudfront.net/',
|
|
$record->request_letter
|
|
);
|
|
|
|
$this->info("Updated: " . $updated);
|
|
|
|
if ($updated !== $record->request_letter) {
|
|
DB::table('neblogistic')
|
|
->where('id', $record->id)
|
|
->update(['request_letter' => $updated]);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function handle()
|
|
{
|
|
$this->repairNullStatuses();
|
|
|
|
// Fetch records from neblogistic where is_sync = 0 (not yet synced)
|
|
$records = DB::table('neblogistic')
|
|
->where('is_sync', 0)
|
|
->orderBy('id', 'desc')
|
|
->limit(5000)
|
|
->get();
|
|
|
|
if ($records->isEmpty()) {
|
|
$records = DB::table('neblogistic')
|
|
->where('is_sync', 2)
|
|
->where('updated_at', '<=', now()->subHour())
|
|
->orderBy('id', 'desc')
|
|
->limit(5000)
|
|
->get();
|
|
}
|
|
|
|
if ($records->isEmpty()) {
|
|
$this->info('No unsynced records found.');
|
|
return 0;
|
|
}
|
|
|
|
// Update all selected records to is_sync = 2 so they are not picked up by another process
|
|
$ids = $records->pluck('id')->all();
|
|
$stampAt = now();
|
|
if (!empty($ids)) {
|
|
DB::table('neblogistic')
|
|
->whereIn('id', $ids)
|
|
->update(['is_sync' => 2, 'updated_at' => $stampAt]);
|
|
|
|
foreach ($records as $record) {
|
|
$oldDataArray = json_decode(json_encode($record), true);
|
|
NeblogisticAuditService::logUpdate(
|
|
$record->id,
|
|
$oldDataArray,
|
|
['is_sync' => 2, 'updated_at' => $stampAt->format('Y-m-d H:i:s')],
|
|
null,
|
|
null
|
|
);
|
|
}
|
|
}
|
|
|
|
// First, fetch all emp_id_tm mappings in one query
|
|
$empIds = $records->pluck('emp_id')->unique()->all();
|
|
// Fetch TMS mappings (without depot_name) first
|
|
$tmsMappings = DB::table('team3_neblogistics_tms')
|
|
->whereIn('emp_id_tm', $empIds)
|
|
->select('emp_id_tm', 'user_id', 'hierarchy_id', 'depot_id', 'tm_email')
|
|
->get()
|
|
->keyBy('emp_id_tm');
|
|
|
|
// Collect all unique depot_ids from TMS mappings
|
|
$depotIds = $tmsMappings->pluck('depot_id')->filter()->unique()->all();
|
|
|
|
// Fetch depot names for these depot_ids
|
|
$depotMappings = [];
|
|
if (!empty($depotIds)) {
|
|
$depotMappings = DB::table('team3_neblogistics_depot')
|
|
->whereIn('id', $depotIds)
|
|
->select('id', 'name')
|
|
->get()
|
|
->keyBy('id');
|
|
}
|
|
|
|
// Pre-fetch all emp_email and tm_email from records and tms for fallback matching
|
|
$empEmails = $records->pluck('emp_email')->filter()->unique()->all();
|
|
$tmsEmailMappings = DB::table('team3_neblogistics_tms')
|
|
->whereIn('tm_email', $empEmails)
|
|
->select('emp_id_tm', 'user_id', 'hierarchy_id', 'depot_id', 'tm_email')
|
|
->get();
|
|
|
|
foreach ($records as $record) {
|
|
// Log the record being processed
|
|
Log::info('Processing Neblogistic record', ['id' => $record->id, 'doctor_name' => $record->doctor_name]);
|
|
|
|
// Lookup mapping from the pre-fetched mapping
|
|
$tms = $tmsMappings->get($record->emp_id);
|
|
|
|
// If not found by emp_id, try to match using emp_email and tm_email
|
|
if (empty($tms) && !empty($record->emp_email)) {
|
|
$tms = $tmsEmailMappings->first(function ($item) use ($record) {
|
|
return strtolower($item->tm_email) === strtolower($record->emp_email);
|
|
});
|
|
}
|
|
|
|
$requestLetter = $record->request_letter;
|
|
if (!empty($requestLetter) && stripos($requestLetter, 'https') === 0) {
|
|
// If already a full https URL, skip this block
|
|
$requestLetterUrl = $requestLetter;
|
|
} elseif (!empty($requestLetter)) {
|
|
// Remove any leading slashes to avoid double slashes in URL
|
|
$requestLetter = ltrim($requestLetter, '/');
|
|
$requestLetterUrl = 'https://d1zmfdp4rdvnc4.cloudfront.net/' . $requestLetter;
|
|
} else {
|
|
// If empty, still set to base URL (with no file)
|
|
$requestLetterUrl = 'https://d1zmfdp4rdvnc4.cloudfront.net/';
|
|
}
|
|
|
|
$requestLetterUrl = str_replace(
|
|
'https://d1zmfdp4rdvnc4.cloudfront.net/d1zmfdp4rdvnc4.cloudfront.net/',
|
|
'https://d1zmfdp4rdvnc4.cloudfront.net/',
|
|
$requestLetterUrl
|
|
);
|
|
$requestLetterUrl = str_replace(
|
|
'https://d1zmfdp4rdvnc4.cloudfront.net/https://did94hhhcgq1d.cloudfront.net/',
|
|
'https://d1zmfdp4rdvnc4.cloudfront.net/',
|
|
$requestLetterUrl
|
|
);
|
|
|
|
// If mapping not found, skip processing and set is_sync = 3
|
|
if (empty($tms)) {
|
|
// Get old data before update for audit trail
|
|
$oldDataArray = json_decode(json_encode($record), true);
|
|
|
|
DB::table('neblogistic')
|
|
->where('id', $record->id)
|
|
->update(['is_sync' => 3, 'updated_at' => now(), 'request_letter' => $requestLetterUrl]);
|
|
|
|
// Log UPDATE operation (console command - no request object)
|
|
NeblogisticAuditService::logUpdate(
|
|
$record->id,
|
|
$oldDataArray,
|
|
['is_sync' => 3, 'updated_at' => now(), 'request_letter' => $requestLetterUrl],
|
|
null,
|
|
null // No request object in console command
|
|
);
|
|
|
|
$this->info("Record ID {$record->id} skipped due to missing mapping (is_sync=3).");
|
|
continue;
|
|
}
|
|
|
|
// Prepare update data with whatever is found
|
|
// Always update request_letter with the full URL
|
|
|
|
// If user_id or hierarchy_id is missing, set is_sync = 3, else is_sync = 1 and update mapping fields
|
|
$oldDataArray = json_decode(json_encode($record), true);
|
|
|
|
if (empty($tms->user_id) || empty($tms->hierarchy_id)) {
|
|
$updateData = [
|
|
'is_sync' => 3,
|
|
'updated_at' => now(),
|
|
'request_letter' => $requestLetterUrl
|
|
];
|
|
|
|
DB::table('neblogistic')
|
|
->where('id', $record->id)
|
|
->update($updateData);
|
|
|
|
// Log UPDATE operation (console command - no request object)
|
|
NeblogisticAuditService::logUpdate(
|
|
$record->id,
|
|
$oldDataArray,
|
|
$updateData,
|
|
null,
|
|
null // No request object in console command
|
|
);
|
|
|
|
$this->info("Record ID {$record->id} skipped due to missing user_id or hierarchy_id (is_sync=3).");
|
|
continue;
|
|
} else {
|
|
$updateData = [
|
|
'is_sync' => 1,
|
|
'updated_at' => now(),
|
|
'user_id' => $tms->user_id,
|
|
'hierarchy_id' => $tms->hierarchy_id,
|
|
'request_letter' => $requestLetterUrl
|
|
];
|
|
if (!empty($tms->depot_id)) {
|
|
$updateData['depot_id'] = $tms->depot_id;
|
|
// Lookup depot name from depotMappings using depot_id
|
|
if (isset($depotMappings[$tms->depot_id])) {
|
|
$updateData['depot_name'] = $depotMappings[$tms->depot_id]->name;
|
|
}
|
|
}
|
|
|
|
DB::table('neblogistic')
|
|
->where('id', $record->id)
|
|
->update($updateData);
|
|
|
|
// Log UPDATE operation (console command - no request object)
|
|
NeblogisticAuditService::logUpdate(
|
|
$record->id,
|
|
$oldDataArray,
|
|
$updateData,
|
|
null,
|
|
null // No request object in console command
|
|
);
|
|
}
|
|
|
|
$this->info("Record ID {$record->id} updated with mapping and marked as synced (is_sync=1).");
|
|
}
|
|
|
|
$this->info('Sync process completed for up to 100 records.');
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Repair neblogistic rows with status NULL using form submission API.
|
|
*/
|
|
private function repairNullStatuses(): void
|
|
{
|
|
$partition = config('neblogistic.partition', 'rapport-team3');
|
|
$formId = config('neblogistic.form_id', '1750914801-153443b9-94d0-4dcb-b3f3-4fa11588a0a9');
|
|
$batch = (int) config('neblogistic.batch_limit', 50);
|
|
$timeout = (int) config('neblogistic.http_timeout', 120);
|
|
|
|
$nullRows = DB::table('neblogistic')
|
|
->whereNull('status')
|
|
->whereNotNull('submission_id')
|
|
->where('submission_id', '!=', '')
|
|
->orderBy('id')
|
|
->limit($batch)
|
|
->get();
|
|
|
|
if ($nullRows->isEmpty()) {
|
|
$this->info('No null-status neblogistic rows to repair.');
|
|
return;
|
|
}
|
|
|
|
$repaired = 0;
|
|
$failed = 0;
|
|
|
|
foreach ($nullRows as $row) {
|
|
try {
|
|
$response = LambdaRequest::getrequest(
|
|
'form',
|
|
'formentity/getsubmissiondetails',
|
|
[
|
|
'partition' => $partition,
|
|
'form_id' => $formId,
|
|
'id' => $row->submission_id,
|
|
],
|
|
'',
|
|
$timeout > 0 ? $timeout : null
|
|
);
|
|
|
|
if (!is_object($response) || ($response->status ?? '') !== 'success') {
|
|
Log::warning('Neblogistic null-status repair: form API did not return success', [
|
|
'neblogistic_id' => $row->id,
|
|
'submission_id' => $row->submission_id,
|
|
]);
|
|
$failed++;
|
|
continue;
|
|
}
|
|
|
|
$submission = $response->data->submission ?? null;
|
|
if (!is_object($submission)) {
|
|
Log::warning('Neblogistic null-status repair: missing submission', [
|
|
'neblogistic_id' => $row->id,
|
|
'submission_id' => $row->submission_id,
|
|
]);
|
|
$failed++;
|
|
continue;
|
|
}
|
|
|
|
$derivedStatus = $this->statusFromSubmission($submission);
|
|
if ($derivedStatus === null) {
|
|
Log::info('Neblogistic null-status repair: could not derive status', [
|
|
'neblogistic_id' => $row->id,
|
|
'submission_id' => $row->submission_id,
|
|
'workflow_status' => $submission->workflow_status ?? null,
|
|
]);
|
|
$failed++;
|
|
continue;
|
|
}
|
|
|
|
$updates = [
|
|
'status' => $derivedStatus,
|
|
'updated_at' => now()->format('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$oldData = json_decode(json_encode($row), true);
|
|
DB::table('neblogistic')->where('id', $row->id)->update($updates);
|
|
|
|
NeblogisticAuditService::logUpdate(
|
|
$row->id,
|
|
$oldData,
|
|
$updates,
|
|
null,
|
|
null
|
|
);
|
|
|
|
$repaired++;
|
|
$this->line(sprintf(
|
|
'Repaired null status neblogistic id=%s submission_id=%s status=%s',
|
|
$row->id,
|
|
$row->submission_id,
|
|
$derivedStatus
|
|
));
|
|
} catch (\Throwable $e) {
|
|
$failed++;
|
|
Log::error('Neblogistic null-status repair: exception', [
|
|
'neblogistic_id' => $row->id ?? null,
|
|
'submission_id' => $row->submission_id ?? null,
|
|
'message' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
$this->info(sprintf('Null-status repair done. Updated: %d, failed/skipped: %d.', $repaired, $failed));
|
|
}
|
|
|
|
/**
|
|
* @param object $submission
|
|
*/
|
|
private function statusFromSubmission($submission): ?string
|
|
{
|
|
$allowed = [
|
|
'Requested',
|
|
'L1 Approved',
|
|
'L2 Approved',
|
|
'L3 Approved',
|
|
'L4 Approved',
|
|
'Rejected',
|
|
'Approved',
|
|
'Dispatched',
|
|
'Delivered',
|
|
'Partially Saved',
|
|
];
|
|
|
|
$formStatus = null;
|
|
if (isset($submission->formdata) && is_object($submission->formdata)) {
|
|
$formStatus = $submission->formdata->status ?? null;
|
|
} elseif (isset($submission->formdata) && is_array($submission->formdata)) {
|
|
$formStatus = $submission->formdata['status'] ?? null;
|
|
}
|
|
|
|
if (is_string($formStatus) && in_array($formStatus, $allowed, true)) {
|
|
return $formStatus === 'Approved' ? 'L1 Approved' : $formStatus;
|
|
}
|
|
|
|
$fromHistory = $this->statusFromApprovalHistory($submission->approval_history ?? null);
|
|
if ($fromHistory !== null) {
|
|
return $fromHistory;
|
|
}
|
|
|
|
switch (strtolower((string) ($submission->workflow_status ?? ''))) {
|
|
case 'rejected':
|
|
return 'Rejected';
|
|
case 'approved':
|
|
return 'L1 Approved';
|
|
case 'unapproved':
|
|
return 'Requested';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mirror metric.js neblogisticStatusFromApprovalHistory (newest first).
|
|
*
|
|
* @param mixed $approvalHistory
|
|
*/
|
|
private function statusFromApprovalHistory($approvalHistory): ?string
|
|
{
|
|
if (!is_array($approvalHistory) || count($approvalHistory) === 0) {
|
|
return null;
|
|
}
|
|
|
|
$known = [
|
|
'L1 Approved' => true,
|
|
'L2 Approved' => true,
|
|
'L3 Approved' => true,
|
|
'L4 Approved' => true,
|
|
'Rejected' => true,
|
|
];
|
|
|
|
$latest = $approvalHistory[0];
|
|
$latest = is_object($latest) ? get_object_vars($latest) : (array) $latest;
|
|
$action = (string) ($latest['action'] ?? '');
|
|
$actionStatus = (string) ($latest['action_status'] ?? '');
|
|
|
|
if ($action === 'reject' || preg_match('/rejected/i', $actionStatus)) {
|
|
return 'Rejected';
|
|
}
|
|
if (isset($known[$actionStatus])) {
|
|
return $actionStatus;
|
|
}
|
|
if (preg_match('/^L([1-4])\s+Approved$/i', $actionStatus, $m)) {
|
|
return 'L' . $m[1] . ' Approved';
|
|
}
|
|
|
|
$maxLevel = 0;
|
|
foreach ($approvalHistory as $entry) {
|
|
$entry = is_object($entry) ? get_object_vars($entry) : (array) $entry;
|
|
$entryAction = (string) ($entry['action'] ?? '');
|
|
$entryStatus = (string) ($entry['action_status'] ?? '');
|
|
if ($entryAction === 'reject' || preg_match('/rejected/i', $entryStatus)) {
|
|
return 'Rejected';
|
|
}
|
|
if (preg_match('/^L([1-4])\s+Approved$/i', $entryStatus, $m)) {
|
|
$maxLevel = max($maxLevel, (int) $m[1]);
|
|
}
|
|
}
|
|
|
|
return $maxLevel > 0 ? ('L' . $maxLevel . ' Approved') : null;
|
|
}
|
|
}
|
|
|
|
// UPDATE `rapport_serverless`.`neblogistic` SET is_sync = 1, depot_name = (SELECT name from team3_neblogistics_depot where team3_neblogistics_depot.id = neblogistic.depot_id ) WHERE `is_sync` = 2 and depot_id IS NOT NULL and user_id is not null and hierarchy_id is not null and depot_name is null;
|