212 lines
7.1 KiB
PHP
212 lines
7.1 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Http\Requests\LambdaRequest;
|
|
use App\Services\NeblogisticAuditService;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class NeblogisticSyncWorkflowStatus extends Command
|
|
{
|
|
protected $signature = 'neblogistic:sync-workflow-status';
|
|
|
|
protected $description = 'Reconcile neblogistic rows stuck as Rejected without rejection metadata using form submission workflow_status';
|
|
|
|
public function handle(): int
|
|
{
|
|
$partition = config('neblogistic.partition', 'rapport-team3');
|
|
$formId = config('neblogistic.form_id', '1750914801-153443b9-94d0-4dcb-b3f3-4fa11588a0a9');
|
|
$batch = (int) config('neblogistic.batch_limit', 500);
|
|
$timeout = (int) config('neblogistic.http_timeout', 120);
|
|
|
|
if (empty($partition) || empty($formId)) {
|
|
$this->error('neblogistic.partition and neblogistic.form_id must be configured.');
|
|
return 1;
|
|
}
|
|
|
|
$records = DB::table('neblogistic')
|
|
->where('status', 'Rejected')
|
|
->whereNull('rejected_at')
|
|
->where(function ($q) {
|
|
$q->whereNull('rejected_reason')->orWhere('rejected_reason', '');
|
|
})
|
|
->whereNotNull('submission_id')
|
|
->where('submission_id', '!=', '')
|
|
->orderBy('id')
|
|
->limit($batch)
|
|
->get();
|
|
|
|
if ($records->isEmpty()) {
|
|
$this->info('No inconsistent neblogistic rows to process.');
|
|
return 0;
|
|
}
|
|
|
|
$processed = 0;
|
|
$failed = 0;
|
|
|
|
foreach ($records as $row) {
|
|
try {
|
|
$params = [
|
|
'partition' => $partition,
|
|
'form_id' => $formId,
|
|
'id' => $row->submission_id,
|
|
];
|
|
|
|
$response = LambdaRequest::getrequest(
|
|
'form',
|
|
'formentity/getsubmissiondetails',
|
|
$params,
|
|
'',
|
|
$timeout > 0 ? $timeout : null
|
|
);
|
|
|
|
if (!is_object($response) || ($response->status ?? '') !== 'success') {
|
|
Log::warning('Neblogistic sync: form API did not return success', [
|
|
'neblogistic_id' => $row->id,
|
|
'submission_id' => $row->submission_id,
|
|
'response' => $response,
|
|
]);
|
|
$failed++;
|
|
continue;
|
|
}
|
|
|
|
$submission = $response->data->submission ?? null;
|
|
if (!is_object($submission)) {
|
|
Log::warning('Neblogistic sync: missing submission in response', [
|
|
'neblogistic_id' => $row->id,
|
|
'submission_id' => $row->submission_id,
|
|
]);
|
|
$failed++;
|
|
continue;
|
|
}
|
|
|
|
$workflowStatus = strtolower((string) ($submission->workflow_status ?? ''));
|
|
|
|
$updates = $this->buildUpdatesFromWorkflow($submission, $workflowStatus);
|
|
if ($updates === null) {
|
|
Log::info('Neblogistic sync: unsupported or empty workflow_status, skipping', [
|
|
'neblogistic_id' => $row->id,
|
|
'submission_id' => $row->submission_id,
|
|
'workflow_status' => $workflowStatus,
|
|
]);
|
|
continue;
|
|
}
|
|
|
|
$oldData = json_decode(json_encode($row), true);
|
|
print_r($updates);
|
|
DB::table('neblogistic')->where('id', $row->id)->update($updates);
|
|
|
|
NeblogisticAuditService::logUpdate(
|
|
$row->id,
|
|
$oldData,
|
|
$updates,
|
|
null,
|
|
null
|
|
);
|
|
|
|
$processed++;
|
|
$this->line(sprintf('Updated neblogistic id=%s submission_id=%s workflow=%s', $row->id, $row->submission_id, $workflowStatus));
|
|
} catch (\Throwable $e) {
|
|
$failed++;
|
|
Log::error('Neblogistic sync: exception', [
|
|
'neblogistic_id' => $row->id ?? null,
|
|
'submission_id' => $row->submission_id ?? null,
|
|
'message' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
$this->info(sprintf('Done. Updated: %d, failed/skipped: %d.', $processed, $failed));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @param object $submission
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
private function buildUpdatesFromWorkflow($submission, string $workflowStatus): ?array
|
|
{
|
|
if ($workflowStatus === '') {
|
|
return null;
|
|
}
|
|
|
|
$now = now()->format('Y-m-d H:i:s');
|
|
$actionDate = $this->parseSubmissionDate($submission);
|
|
|
|
switch ($workflowStatus) {
|
|
case 'rejected':
|
|
return [
|
|
'status' => 'Rejected',
|
|
'rejected_at' => $actionDate ? $actionDate->format('Y-m-d H:i:s') : $now,
|
|
'rejected_reason' => $this->latestRejectReason($submission) ?? 'Synced from form workflow',
|
|
'updated_at' => $now,
|
|
];
|
|
|
|
case 'approved':
|
|
return [
|
|
'status' => 'L1 Approved',
|
|
'l1_approved_at' => $actionDate ? $actionDate->format('Y-m-d H:i:s') : $now,
|
|
'rejected_at' => null,
|
|
'rejected_reason' => null,
|
|
'updated_at' => $now,
|
|
];
|
|
|
|
case 'unapproved':
|
|
return [
|
|
'status' => 'Requested',
|
|
'l1_approved_at' => null,
|
|
'rejected_at' => null,
|
|
'rejected_reason' => null,
|
|
'updated_at' => $now,
|
|
];
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param object $submission
|
|
*/
|
|
private function parseSubmissionDate($submission): ?Carbon
|
|
{
|
|
$raw = $submission->workflow_action_date ?? null;
|
|
if ($raw === null || $raw === '') {
|
|
return null;
|
|
}
|
|
try {
|
|
return Carbon::parse($raw);
|
|
} catch (\Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* approval_history is ordered with newest first (unshift on approve/reject).
|
|
*
|
|
* @param object $submission
|
|
*/
|
|
private function latestRejectReason($submission): ?string
|
|
{
|
|
$history = $submission->approval_history ?? null;
|
|
if (!is_array($history)) {
|
|
return null;
|
|
}
|
|
foreach ($history as $entry) {
|
|
$entry = is_object($entry) ? get_object_vars($entry) : (array) $entry;
|
|
$action = strtolower((string) ($entry['action'] ?? ''));
|
|
if ($action !== 'reject') {
|
|
continue;
|
|
}
|
|
$reason = trim((string) ($entry['reason'] ?? ''));
|
|
return $reason !== '' ? $reason : null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|