inspiren-sem-tool/app/Console/Commands/PendingProjectActivitiesNotify.php
brian-inspiren 221d3f8173
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
feat: sem codebase
2026-05-21 11:28:03 +08:00

157 lines
6.5 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Models\ClientProjectActivities;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class PendingProjectActivitiesNotify extends Command
{
protected $signature = 'project-activities:pending-notify {--test-email= : Send a sample reminder email to this address only}';
protected $description = 'Notify users about pending project activities based on estimated completion date';
public function handle(): int
{
try {
if ($this->option('test-email')) {
return $this->sendTestEmail($this->option('test-email'));
}
$today = Carbon::today();
$tomorrow = Carbon::tomorrow();
$activities = ClientProjectActivities::query()
->with(['client:id,name,customer_id', 'user:id,name,email'])
->whereNull('completed_at')
->whereNotNull('estimated_completed_at')
->whereDate('estimated_completed_at', '<=', $tomorrow)
->orderBy('estimated_completed_at')
->get();
$activities
->groupBy('user_id')
->each(function ($userActivities, $userId) use ($today, $tomorrow) {
$user = $userActivities->first()->user;
if (! $user || empty($user->email)) {
$this->warn("Skipping project activity reminder for user_id={$userId}; no valid email found.");
return;
}
$overdue = $userActivities->filter(function (ClientProjectActivities $activity) use ($today) {
return $activity->estimated_completed_at->lt($today);
});
$dueToday = $userActivities->filter(function (ClientProjectActivities $activity) use ($today) {
return $activity->estimated_completed_at->isSameDay($today);
});
$dueTomorrow = $userActivities->filter(function (ClientProjectActivities $activity) use ($tomorrow) {
return $activity->estimated_completed_at->isSameDay($tomorrow);
});
if ($overdue->isEmpty() && $dueToday->isEmpty()) {
return;
}
$mailData = [
'user_name' => $user->name,
'role_label' => 'Activity Owner',
'overdue' => $overdue->map(fn (ClientProjectActivities $activity) => $this->formatActivity($activity, $today))->values()->all(),
'due_today' => $dueToday->map(fn (ClientProjectActivities $activity) => $this->formatActivity($activity, $today))->values()->all(),
'due_tomorrow' => $dueTomorrow->map(fn (ClientProjectActivities $activity) => $this->formatActivity($activity, $today))->values()->all(),
];
Mail::send('mail.project_activity_reminder', ['data' => $mailData], function ($message) use ($user) {
$message->to($user->email, $user->name)
->subject('Pending Project Activity Reminder');
});
$this->info("Sent pending project activity reminder to {$user->email} ({$user->name}).");
});
return self::SUCCESS;
} catch (\Throwable $e) {
Log::error('Error sending pending project activity reminders: '.$e->getMessage(), [
'trace' => $e->getTraceAsString(),
]);
$this->error('Failed to send pending project activity reminders.');
return self::FAILURE;
}
}
private function formatActivity(ClientProjectActivities $activity, Carbon $today): array
{
$estimatedDate = $activity->estimated_completed_at->copy()->startOfDay();
return [
'activity_no' => $activity->activity_no,
'customer_id' => $activity->client?->customer_id ?? '-',
'customer' => $activity->client?->name ?? '-',
'activity_type' => $activity->activity_type ?? '-',
'task_description' => $activity->task_description ?? '-',
'estimated_completed_at' => $estimatedDate->format('Y-m-d'),
'number_of_days' => (int) $today->diffInDays($estimatedDate, false),
];
}
private function sendTestEmail(string $email): int
{
$today = Carbon::today();
$mailData = [
'user_name' => 'Brian',
'role_label' => 'Activity Owner',
'overdue' => [
[
'activity_no' => 'ACT0001',
'customer_id' => '1234567890',
'customer' => 'Example Client A',
'activity_type' => 'scheduled',
'task_description' => 'Review SEM performance report and update client notes.',
'estimated_completed_at' => $today->copy()->subDays(2)->format('Y-m-d'),
'number_of_days' => -2,
],
],
'due_today' => [
[
'activity_no' => 'ACT0002',
'customer_id' => '1234567891',
'customer' => 'Example Client B',
'activity_type' => 'scheduled',
'task_description' => 'Prepare optimisation checklist for campaign review.',
'estimated_completed_at' => $today->format('Y-m-d'),
'number_of_days' => 0,
],
],
'due_tomorrow' => [
[
'activity_no' => 'ACT0003',
'customer_id' => '1234567892',
'customer' => 'Example Client C',
'activity_type' => 'scheduled',
'task_description' => 'Follow up on budget pacing and next-month planning.',
'estimated_completed_at' => $today->copy()->addDay()->format('Y-m-d'),
'number_of_days' => 1,
],
],
];
Mail::send('mail.project_activity_reminder', ['data' => $mailData], function ($message) use ($email) {
$message->to($email)
->subject('Test: Pending Project Activity Reminder');
});
$this->info("Sent test pending project activity reminder to {$email}.");
return self::SUCCESS;
}
}