Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
InviteMemberAction
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 2
20
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Organizations\Application\Actions;
6
7use Illuminate\Support\Str;
8use Illuminate\Validation\ValidationException;
9use ImovelZapAi\Authentication\Domain\Enums\SystemRole;
10use ImovelZapAi\Organizations\Application\Services\PlanLimitGuard;
11use ImovelZapAi\Organizations\Domain\Enums\InvitationStatus;
12use ImovelZapAi\Organizations\Infrastructure\Persistence\Eloquent\OrganizationInvitationModel;
13use ImovelZapAi\Tenancy\Domain\Enums\OrganizationType;
14use ImovelZapAi\Tenancy\Infrastructure\Persistence\Eloquent\TenantModel;
15use App\Models\User;
16
17final class InviteMemberAction
18{
19    public function __construct(
20        private readonly PlanLimitGuard $planLimitGuard,
21        private readonly AssignOrganizationPlanAction $assignPlan,
22    ) {}
23
24    public function execute(
25        string $tenantId,
26        string $email,
27        SystemRole $role,
28        string $invitedByUserId,
29    ): OrganizationInvitationModel {
30        $email = Str::lower(trim($email));
31        $tenant = TenantModel::query()->findOrFail($tenantId);
32
33        if ($tenant->type === OrganizationType::Solo) {
34            $tenant->forceFill(['type' => OrganizationType::Agency])->save();
35            $this->assignPlan->execute($tenantId, 'start', \ImovelZapAi\Organizations\Domain\Enums\SubscriptionStatus::Trialing);
36        }
37
38        $this->planLimitGuard->assertCanAddUser($tenantId);
39
40        if (User::query()->where('tenant_id', $tenantId)->where('email', $email)->exists()) {
41            throw ValidationException::withMessages([
42                'email' => ['Este e-mail já faz parte da organização.'],
43            ]);
44        }
45
46        OrganizationInvitationModel::query()
47            ->where('tenant_id', $tenantId)
48            ->where('email', $email)
49            ->where('status', InvitationStatus::Pending)
50            ->update(['status' => InvitationStatus::Revoked]);
51
52        return OrganizationInvitationModel::query()->create([
53            'tenant_id' => $tenantId,
54            'email' => $email,
55            'role' => $role,
56            'token' => Str::random(48),
57            'status' => InvitationStatus::Pending,
58            'invited_by_user_id' => $invitedByUserId,
59            'expires_at' => now()->addDays((int) config('organizations.invitation_expires_days', 7)),
60        ]);
61    }
62}