Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
1.64% covered (danger)
1.64%
1 / 61
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ResendCompanyInvitationJob
1.64% covered (danger)
1.64%
1 / 61
33.33% covered (danger)
33.33%
1 / 3
126.15
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 backoff
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 handle
0.00% covered (danger)
0.00%
0 / 59
0.00% covered (danger)
0.00%
0 / 1
90
1<?php
2
3namespace App\Jobs;
4
5use App\Http\Models\Admin\AdminUserInvitation;
6use App\Http\Models\Admin\Company;
7use App\Http\Models\Admin\CompanyPOC;
8use App\Http\Models\Auth\User;
9use App\Http\Services\Admin\Companies\CompanyService;
10use App\Jobs\Emails\AddNewUserReminder;
11use App\Mail\AdminCenterAddExistingUser;
12use App\Mail\AdminCenterAddNewUser;
13use App\Services\Email\EmailService;
14use Carbon\Carbon;
15use Illuminate\Bus\Queueable;
16use Illuminate\Contracts\Queue\ShouldQueue;
17use Illuminate\Foundation\Bus\Dispatchable;
18use Illuminate\Queue\InteractsWithQueue;
19use Illuminate\Queue\SerializesModels;
20use Illuminate\Support\Str;
21
22/**
23 * Job that resends a single pending invitation email asynchronously.
24 *
25 * Extracts and replicates the per-invitation logic from
26 * ClientManagementUsersService::resendInvitations() so that bulk resends
27 * for a company can be processed in parallel without blocking the HTTP response.
28 *
29 * Handles three invitation cases:
30 * 1. Company POC — delegates to CompanyService::sendInvitationMail()
31 * 2. Existing user (account created >1 min before invitation) — sends AdminCenterAddExistingUser
32 * 3. New user — sends AdminCenterAddNewUser + schedules 48h AddNewUserReminder
33 */
34class ResendCompanyInvitationJob implements ShouldQueue
35{
36    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
37
38    /**
39     * Number of times to attempt the job before failing.
40     *
41     * @var int
42     */
43    public $tries = 5;
44
45    /**
46     * Create a new job instance.
47     *
48     * @param  string  $inviteeEmail  The email address of the invited user
49     * @param  string  $adminEmail  Email of the admin triggering the resend (used as inviter)
50     * @param  Company  $company  The company the invitation belongs to
51     */
52    public function __construct(
53        private readonly string $inviteeEmail,
54        private readonly string $adminEmail,
55        private readonly Company $company,
56    ) {}
57
58    /**
59     * Get the number of seconds to wait before retrying the job.
60     *
61     * @return array<int, int>
62     */
63    public function backoff(): array
64    {
65        return [10, 30, 60, 120, 300];
66    }
67
68    /**
69     * Execute the job.
70     *
71     * Regenerates a temporary password, updates the invitation and user records,
72     * and dispatches the appropriate email based on user state.
73     *
74     * @param  EmailService  $emailService  Service for sending transactional emails
75     * @param  CompanyService  $companyService  Service for company-level invitation operations
76     */
77    public function handle(EmailService $emailService, CompanyService $companyService): void
78    {
79        $email = strtolower($this->inviteeEmail);
80
81        $invitation = AdminUserInvitation::firstWhere('email', $email);
82        $user = User::where('email', $email)->whereStatus('Invited')->first();
83        $pocUser = CompanyPOC::where('email', $email)->first();
84
85        $password = Str::random(16);
86        $encryptedPassword = bcrypt($password);
87        $passwordExpiry = Carbon::now()->addDays(7);
88        $passwordExpiryLabel = $passwordExpiry->format('m/d/Y').' at '.$passwordExpiry->format('h:i A');
89
90        $existingUser = $user && $invitation
91            && $user->created_at->diffInMinutes($invitation->created_at) > 1;
92
93        if ($invitation) {
94            $invitation->email = $email;
95            $invitation->password = $encryptedPassword;
96            $invitation->temp_password = $encryptedPassword;
97            $invitation->temp_password_expiry = $passwordExpiry->toDateTimeString();
98            $invitation->save();
99        }
100
101        if (! $existingUser && $user) {
102            $user->password = $encryptedPassword;
103            $user->temp_password = $encryptedPassword;
104            $user->temp_password_expiry = $passwordExpiry->toDateTimeString();
105            $user->save();
106        }
107
108        if ($pocUser) {
109            $companyService->sendInvitationMail($email, $user, $existingUser, $password);
110
111            return;
112        }
113
114        if ($existingUser) {
115            $emailService->send(
116                $user->email,
117                new AdminCenterAddExistingUser(
118                    admin_email: $this->adminEmail,
119                    company: $this->company->name,
120                    email: $user->email,
121                ),
122                'cmc_resend_invitation_existing'
123            );
124        } else {
125            $invitation->password = $encryptedPassword;
126            $invitation->temp_password = $encryptedPassword;
127            $invitation->temp_password_expiry = $passwordExpiry->toDateTimeString();
128            $invitation->admin_email = $this->adminEmail;
129
130            $emailService->send(
131                $email,
132                new AdminCenterAddNewUser(
133                    email: $email,
134                    password: $password,
135                    inviter: $this->adminEmail,
136                    password_expiry: $passwordExpiryLabel,
137                ),
138                'cmc_resend_invitation_new'
139            );
140
141            $addNewUserEmailJob = (new AddNewUserReminder(
142                $email,
143                $password,
144                $this->adminEmail,
145                $passwordExpiryLabel,
146                $emailService
147            ))->delay(now()->addHours(48));
148
149            $job = app(\Illuminate\Contracts\Bus\Dispatcher::class)->dispatch($addNewUserEmailJob);
150
151            if ($invitation) {
152                $invitation->reminder_job_id = $job;
153                $invitation->save();
154            }
155        }
156    }
157}