458 lines
16 KiB
PHP
458 lines
16 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\Mail;
|
|
use App\Models\User;
|
|
use App\Models\Organization;
|
|
use App\Models\Admin;
|
|
use App\Models\Bank;
|
|
use App\Models\Account;
|
|
use App\Mail\CallBlockedMail;
|
|
use App\Mail\CallExpiredMail;
|
|
use App\Mail\CallExpiringMail;
|
|
use App\Mail\InactiveProfileWarning1Mail;
|
|
use App\Models\Call;
|
|
use App\Mail\InactiveProfileWarning2Mail;
|
|
use App\Mail\InactiveProfileWarningFinalMail;
|
|
use App\Mail\UserDeletedMail;
|
|
use App\Mail\TransferReceived;
|
|
use App\Mail\ProfileLinkChangedMail;
|
|
use App\Mail\ProfileEditedByAdminMail;
|
|
use App\Mail\VerifyProfileEmailMailable;
|
|
use App\Mail\ReservationCreatedMail;
|
|
use App\Mail\ReservationCancelledMail;
|
|
use App\Mail\ReservationUpdateMail;
|
|
use App\Mail\ReactionCreatedMail;
|
|
use App\Mail\TagAddedMail;
|
|
|
|
class SendTestEmail extends Command
|
|
{
|
|
protected $signature = 'email:send-test
|
|
{--type= : Email type to send (use --list to see all types)}
|
|
{--receiver= : Receiver type (user, organization, admin, bank)}
|
|
{--id= : Receiver ID}
|
|
{--list : List all available email types}
|
|
{--queue : Send via queue instead of immediately}';
|
|
|
|
protected $description = 'Send test mailing transactional emails for testing and review';
|
|
|
|
protected $emailTypes = [
|
|
'inactive-warning-1' => [
|
|
'class' => InactiveProfileWarning1Mail::class,
|
|
'description' => 'Inactive profile warning 1 (first warning)',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'inactive-warning-2' => [
|
|
'class' => InactiveProfileWarning2Mail::class,
|
|
'description' => 'Inactive profile warning 2 (second warning)',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'inactive-warning-final' => [
|
|
'class' => InactiveProfileWarningFinalMail::class,
|
|
'description' => 'Inactive Profile Final Warning (last warning)',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'user-deleted' => [
|
|
'class' => UserDeletedMail::class,
|
|
'description' => 'User Deleted Notification',
|
|
'supports' => ['user'],
|
|
],
|
|
'transfer-received' => [
|
|
'class' => TransferReceived::class,
|
|
'description' => 'Transfer/Payment Received Notification',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'profile-link-changed' => [
|
|
'class' => ProfileLinkChangedMail::class,
|
|
'description' => 'Profile Link/Name Changed Notification',
|
|
'supports' => ['user', 'organization', 'admin', 'bank'],
|
|
],
|
|
'profile-edited-by-admin' => [
|
|
'class' => ProfileEditedByAdminMail::class,
|
|
'description' => 'Profile Edited by Admin Notification',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'verify-email' => [
|
|
'class' => VerifyProfileEmailMailable::class,
|
|
'description' => 'Email Verification Request',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'reservation-created' => [
|
|
'class' => ReservationCreatedMail::class,
|
|
'description' => 'Reservation Created Notification',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'reservation-cancelled' => [
|
|
'class' => ReservationCancelledMail::class,
|
|
'description' => 'Reservation Cancelled Notification',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'reservation-updated' => [
|
|
'class' => ReservationUpdateMail::class,
|
|
'description' => 'Reservation Updated Notification',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'reaction-created' => [
|
|
'class' => ReactionCreatedMail::class,
|
|
'description' => 'Reaction/Comment Created Notification',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'tag-added' => [
|
|
'class' => TagAddedMail::class,
|
|
'description' => 'Tag Added to Profile Notification',
|
|
'supports' => ['user', 'organization'],
|
|
],
|
|
'call-expired' => [
|
|
'class' => CallExpiredMail::class,
|
|
'description' => 'Call Expired Notification',
|
|
'supports' => ['user', 'organization', 'bank'],
|
|
],
|
|
'call-expiring' => [
|
|
'class' => CallExpiringMail::class,
|
|
'description' => 'Call Expiring Soon Warning',
|
|
'supports' => ['user', 'organization', 'bank'],
|
|
],
|
|
'call-blocked' => [
|
|
'class' => CallBlockedMail::class,
|
|
'description' => 'Call Blocked by Admin Notification',
|
|
'supports' => ['user', 'organization', 'bank'],
|
|
],
|
|
];
|
|
|
|
public function handle()
|
|
{
|
|
if ($this->option('list')) {
|
|
return $this->listEmailTypes();
|
|
}
|
|
|
|
$type = $this->option('type');
|
|
$receiverType = $this->option('receiver');
|
|
$receiverId = $this->option('id');
|
|
|
|
// Interactive mode if no options provided
|
|
if (!$type || !$receiverType || !$receiverId) {
|
|
return $this->interactiveMode();
|
|
}
|
|
|
|
return $this->sendEmail($type, $receiverType, $receiverId);
|
|
}
|
|
|
|
protected function listEmailTypes()
|
|
{
|
|
$this->info('Available Email Types:');
|
|
$this->newLine();
|
|
|
|
foreach ($this->emailTypes as $key => $config) {
|
|
$supports = implode(', ', $config['supports']);
|
|
$this->line(" <fg=cyan>{$key}</>");
|
|
$this->line(" Description: {$config['description']}");
|
|
$this->line(" Supports: {$supports}");
|
|
$this->newLine();
|
|
}
|
|
|
|
$this->info('Usage Example:');
|
|
$this->line(' php artisan email:send-test --type=inactive-warning-1 --receiver=user --id=102');
|
|
$this->newLine();
|
|
|
|
return 0;
|
|
}
|
|
|
|
protected function interactiveMode()
|
|
{
|
|
$this->info('📧 Test Email Sender - Interactive Mode');
|
|
$this->newLine();
|
|
|
|
// Select email type
|
|
$typeChoices = array_map(
|
|
fn ($key, $config) => "{$key} - {$config['description']}",
|
|
array_keys($this->emailTypes),
|
|
array_values($this->emailTypes)
|
|
);
|
|
|
|
$selectedIndex = array_search(
|
|
$this->choice('Select email type', $typeChoices),
|
|
$typeChoices
|
|
);
|
|
$type = array_keys($this->emailTypes)[$selectedIndex];
|
|
|
|
// Select receiver type
|
|
$supports = $this->emailTypes[$type]['supports'];
|
|
$receiverType = $this->choice('Select receiver type', $supports);
|
|
|
|
// Enter receiver ID
|
|
$receiverId = $this->ask('Enter receiver ID');
|
|
|
|
return $this->sendEmail($type, $receiverType, $receiverId);
|
|
}
|
|
|
|
protected function sendEmail($type, $receiverType, $receiverId)
|
|
{
|
|
if (!isset($this->emailTypes[$type])) {
|
|
$this->error("Invalid email type: {$type}");
|
|
$this->info('Use --list to see all available types');
|
|
return 1;
|
|
}
|
|
|
|
$config = $this->emailTypes[$type];
|
|
|
|
if (!in_array($receiverType, $config['supports'])) {
|
|
$this->error("Email type '{$type}' does not support receiver type '{$receiverType}'");
|
|
$this->info('Supported types: ' . implode(', ', $config['supports']));
|
|
return 1;
|
|
}
|
|
|
|
// Get receiver profile
|
|
$receiver = $this->getReceiver($receiverType, $receiverId);
|
|
if (!$receiver) {
|
|
$this->error("Receiver not found: {$receiverType} #{$receiverId}");
|
|
return 1;
|
|
}
|
|
|
|
$this->info("Sending '{$type}' email to {$receiver->name} ({$receiver->email})");
|
|
$this->newLine();
|
|
|
|
try {
|
|
$mailable = $this->buildMailable($type, $receiver, $receiverType);
|
|
|
|
if ($this->option('queue')) {
|
|
Mail::to($receiver->email)->queue($mailable);
|
|
$this->info('✅ Email queued successfully');
|
|
$this->line('Run queue worker: php artisan queue:work --stop-when-empty');
|
|
} else {
|
|
Mail::to($receiver->email)->send($mailable);
|
|
$this->info('✅ Email sent successfully');
|
|
}
|
|
|
|
$this->newLine();
|
|
$this->line("Recipient: {$receiver->email}");
|
|
$this->line("Profile: {$receiver->name}");
|
|
$this->line("Language: " . ($receiver->lang_preference ?? 'en'));
|
|
|
|
return 0;
|
|
} catch (\Exception $e) {
|
|
$this->error('Failed to send email: ' . $e->getMessage());
|
|
$this->line($e->getTraceAsString());
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
protected function getReceiver($type, $id)
|
|
{
|
|
return match($type) {
|
|
'user' => User::find($id),
|
|
'organization' => Organization::find($id),
|
|
'admin' => Admin::find($id),
|
|
'bank' => Bank::find($id),
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
protected function buildMailable($type, $receiver, $receiverType)
|
|
{
|
|
// Get test data
|
|
$accounts = $this->getAccountsData($receiver);
|
|
$totalBalance = $this->getTotalBalance($accounts);
|
|
|
|
return match($type) {
|
|
'inactive-warning-1' => new InactiveProfileWarning1Mail(
|
|
$receiver,
|
|
ucfirst($receiverType),
|
|
'2 weeks',
|
|
14,
|
|
$accounts,
|
|
$totalBalance,
|
|
351
|
|
),
|
|
'inactive-warning-2' => new InactiveProfileWarning2Mail(
|
|
$receiver,
|
|
ucfirst($receiverType),
|
|
'1 week',
|
|
7,
|
|
$accounts,
|
|
$totalBalance,
|
|
358
|
|
),
|
|
'inactive-warning-final' => new InactiveProfileWarningFinalMail(
|
|
$receiver,
|
|
ucfirst($receiverType),
|
|
'24 hours',
|
|
1,
|
|
$accounts,
|
|
$totalBalance,
|
|
365
|
|
),
|
|
'user-deleted' => new UserDeletedMail(
|
|
$receiver,
|
|
$accounts,
|
|
$totalBalance,
|
|
$this->getTransferTargetAccount()
|
|
),
|
|
'transfer-received' => $this->buildTransferReceivedMail($receiver),
|
|
'profile-link-changed' => new ProfileLinkChangedMail(
|
|
$receiver,
|
|
$this->getLinkedProfileForTest($receiver),
|
|
'attached'
|
|
),
|
|
'profile-edited-by-admin' => new ProfileEditedByAdminMail(
|
|
$receiver,
|
|
'Test Admin',
|
|
'Updated profile information for testing purposes'
|
|
),
|
|
'verify-email' => new VerifyProfileEmailMailable(
|
|
$receiver->email,
|
|
url('/verify-email/' . base64_encode($receiver->email))
|
|
),
|
|
'reservation-created' => $this->buildReservationMail($receiver, ReservationCreatedMail::class),
|
|
'reservation-cancelled' => $this->buildReservationMail($receiver, ReservationCancelledMail::class),
|
|
'reservation-updated' => $this->buildReservationMail($receiver, ReservationUpdateMail::class),
|
|
'reaction-created' => $this->buildReactionMail($receiver),
|
|
'tag-added' => $this->buildTagAddedMail($receiver),
|
|
'call-expired' => $this->buildCallExpiredMail($receiver, $receiverType),
|
|
'call-expiring' => $this->buildCallExpiringMail($receiver, $receiverType),
|
|
'call-blocked' => $this->buildCallBlockedMail($receiver, $receiverType),
|
|
default => throw new \Exception("Mailable builder not implemented for type: {$type}"),
|
|
};
|
|
}
|
|
|
|
protected function getAccountsData($profile)
|
|
{
|
|
$accounts = [];
|
|
$profileAccounts = $profile->accounts()->active()->notRemoved()->get();
|
|
|
|
foreach ($profileAccounts as $account) {
|
|
\Cache::forget("account_balance_{$account->id}");
|
|
$accounts[] = [
|
|
'id' => $account->id,
|
|
'name' => $account->name,
|
|
'balance' => $account->balance,
|
|
'balanceFormatted' => tbFormat($account->balance),
|
|
];
|
|
}
|
|
|
|
return $accounts;
|
|
}
|
|
|
|
protected function getTotalBalance($accounts)
|
|
{
|
|
return array_sum(array_column($accounts, 'balance'));
|
|
}
|
|
|
|
protected function getTransferTargetAccount()
|
|
{
|
|
// Get a random organization account or create test data
|
|
$account = Account::whereHasMorph('accountable', [Organization::class])
|
|
->active()
|
|
->notRemoved()
|
|
->first();
|
|
|
|
return $account ? [
|
|
'id' => $account->id,
|
|
'name' => $account->name,
|
|
] : [
|
|
'id' => 1,
|
|
'name' => 'Test Organization Account',
|
|
];
|
|
}
|
|
|
|
protected function buildTransferReceivedMail($receiver)
|
|
{
|
|
$senderAccount = $receiver->accounts()->active()->notRemoved()->first();
|
|
|
|
if (!$senderAccount) {
|
|
throw new \Exception('Receiver has no active accounts');
|
|
}
|
|
|
|
return new TransferReceived(
|
|
$receiver->name,
|
|
120, // 2 hours in minutes
|
|
tbFormat(120),
|
|
'Test Transfer',
|
|
$senderAccount->name,
|
|
$receiver->email,
|
|
url('/profile/' . $receiver->name)
|
|
);
|
|
}
|
|
|
|
protected function buildReservationMail($receiver, $mailClass)
|
|
{
|
|
$postTitle = 'Test Post - ' . $mailClass;
|
|
$postOwner = 'Test Post Owner';
|
|
$postUrl = url('/posts/test-post');
|
|
$reservationDate = now()->addDays(7)->format('Y-m-d H:i');
|
|
|
|
return new $mailClass(
|
|
$receiver->name,
|
|
$postTitle,
|
|
$postOwner,
|
|
$postUrl,
|
|
$reservationDate
|
|
);
|
|
}
|
|
|
|
protected function buildReactionMail($receiver)
|
|
{
|
|
return new ReactionCreatedMail(
|
|
$receiver->name,
|
|
'Test Commenter',
|
|
'Test Post Title',
|
|
'This is a test comment for email testing purposes.',
|
|
url('/posts/test-post')
|
|
);
|
|
}
|
|
|
|
protected function buildTagAddedMail($receiver)
|
|
{
|
|
return new TagAddedMail(
|
|
$receiver->name,
|
|
'Test Tag',
|
|
'Test Admin',
|
|
url('/profile/' . $receiver->name)
|
|
);
|
|
}
|
|
|
|
protected function getTestCall($receiver): Call
|
|
{
|
|
return Call::where('callable_type', get_class($receiver))
|
|
->where('callable_id', $receiver->id)
|
|
->with(['tag'])
|
|
->first()
|
|
?? Call::with(['tag'])->first()
|
|
?? throw new \Exception('No calls found for test');
|
|
}
|
|
|
|
protected function buildCallExpiredMail($receiver, $receiverType): CallExpiredMail
|
|
{
|
|
return new CallExpiredMail($this->getTestCall($receiver), $receiver, ucfirst($receiverType));
|
|
}
|
|
|
|
protected function buildCallExpiringMail($receiver, $receiverType): CallExpiringMail
|
|
{
|
|
return new CallExpiringMail($this->getTestCall($receiver), $receiver, ucfirst($receiverType), 7);
|
|
}
|
|
|
|
protected function buildCallBlockedMail($receiver, $receiverType): CallBlockedMail
|
|
{
|
|
return new CallBlockedMail($this->getTestCall($receiver), $receiver, ucfirst($receiverType));
|
|
}
|
|
|
|
protected function getLinkedProfileForTest($receiver)
|
|
{
|
|
// Find a different profile type to use as the linked profile
|
|
// If receiver is a User, find an Organization/Admin/Bank to link
|
|
// If receiver is Organization/Admin/Bank, find a User to link
|
|
|
|
if ($receiver instanceof User) {
|
|
// Try to find an organization first, then admin, then bank
|
|
$linked = Organization::first() ?? Admin::first() ?? Bank::first();
|
|
} else {
|
|
// For Organization/Admin/Bank, find a user
|
|
$linked = User::first();
|
|
}
|
|
|
|
// If we can't find any other profile, just return the same receiver
|
|
return $linked ?? $receiver;
|
|
}
|
|
}
|