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;