Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
70 / 70
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
CreateSignatureRequestAction
100.00% covered (success)
100.00%
70 / 70
100.00% covered (success)
100.00%
2 / 2
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
69 / 69
100.00% covered (success)
100.00%
1 / 1
10
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Signatures\Application\Actions;
6
7use App\Models\User;
8use Illuminate\Support\Facades\DB;
9use Illuminate\Support\Str;
10use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\BusinessDocumentModel;
11use ImovelZapAi\Signatures\Application\Services\SignatureAuditWriter;
12use ImovelZapAi\Signatures\Application\Services\SignatureStatusSynchronizer;
13use ImovelZapAi\Signatures\Domain\Enums\SignatureAuditEvent;
14use ImovelZapAi\Signatures\Domain\Enums\SignatureMode;
15use ImovelZapAi\Signatures\Domain\Enums\SignatureProviderKey;
16use ImovelZapAi\Signatures\Domain\Enums\SignatureRequestStatus;
17use ImovelZapAi\Signatures\Domain\Enums\SignerRole;
18use ImovelZapAi\Signatures\Domain\Enums\SignerStatus;
19use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignatureRequestModel;
20use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignatureStepModel;
21use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignerModel;
22
23final class CreateSignatureRequestAction
24{
25    public function __construct(
26        private readonly SignatureAuditWriter $audit,
27        private readonly SignatureStatusSynchronizer $sync,
28    ) {}
29
30    /**
31     * @param  array{
32     *     business_document_id: string,
33     *     provider?: string|null,
34     *     mode?: string|null,
35     *     title?: string|null,
36     *     message?: string|null,
37     *     expires_at?: string|null,
38     *     client_portal_id?: string|null,
39     *     deal_id?: string|null,
40     *     signers: list<array{
41     *         name: string,
42     *         email?: string|null,
43     *         phone?: string|null,
44     *         contact_id?: string|null,
45     *         role?: string|null,
46     *         order?: int|null
47     *     }>
48     * }  $data
49     */
50    public function execute(string $tenantId, User $actor, array $data): SignatureRequestModel
51    {
52        return DB::transaction(function () use ($tenantId, $actor, $data): SignatureRequestModel {
53            if (empty($data['signers']) || ! is_array($data['signers'])) {
54                throw \ImovelZapAi\Signatures\Domain\Exceptions\SignatureException::invalidState('informe ao menos um signatário.');
55            }
56
57            $document = BusinessDocumentModel::query()
58                ->where('tenant_id', $tenantId)
59                ->whereKey($data['business_document_id'])
60                ->firstOrFail();
61
62            $provider = ! empty($data['provider'])
63                ? SignatureProviderKey::from($data['provider'])
64                : SignatureProviderKey::from((string) config('signatures.default_provider', 'native'));
65
66            $mode = ! empty($data['mode'])
67                ? SignatureMode::from($data['mode'])
68                : SignatureMode::Parallel;
69
70            $expiresAt = ! empty($data['expires_at'])
71                ? $data['expires_at']
72                : now()->addDays((int) config('signatures.token_ttl_days', 14));
73
74            $request = SignatureRequestModel::query()->create([
75                'tenant_id' => $tenantId,
76                'business_document_id' => $document->id,
77                'deal_id' => $data['deal_id'] ?? $document->deal_id,
78                'client_portal_id' => $data['client_portal_id'] ?? $document->client_portal_id,
79                'provider' => $provider,
80                'mode' => $mode,
81                'status' => SignatureRequestStatus::Draft,
82                'title' => $data['title'] ?? ($document->title ?: 'Assinatura de documento'),
83                'message' => $data['message'] ?? null,
84                'expires_at' => $expiresAt,
85                'created_by' => $actor->id,
86                'responsible_user_id' => $actor->id,
87            ]);
88
89            foreach ($data['signers'] as $index => $signerData) {
90                $plain = Str::lower(Str::random(40));
91                $order = $signerData['order'] ?? ($index + 1);
92                $role = ! empty($signerData['role'])
93                    ? SignerRole::from($signerData['role'])
94                    : SignerRole::Buyer;
95
96                SignerModel::query()->create([
97                    'tenant_id' => $tenantId,
98                    'signature_request_id' => $request->id,
99                    'contact_id' => $signerData['contact_id'] ?? null,
100                    'name' => $signerData['name'],
101                    'email' => $signerData['email'] ?? null,
102                    'phone' => $signerData['phone'] ?? null,
103                    'role' => $role,
104                    'order' => $order,
105                    'status' => SignerStatus::Pending,
106                    'access_token_hash' => hash('sha256', $plain),
107                    'access_token_prefix' => substr($plain, 0, 8),
108                ]);
109            }
110
111            $request->load('signers');
112            foreach ($request->signers as $signer) {
113                SignatureStepModel::query()->create([
114                    'tenant_id' => $tenantId,
115                    'signature_request_id' => $request->id,
116                    'order_index' => $signer->order,
117                    'label' => $signer->name,
118                    'status' => 'pending',
119                ]);
120            }
121
122            $this->audit->write(
123                $tenantId,
124                $request->id,
125                SignatureAuditEvent::Created,
126                actorType: 'broker',
127                actorUserId: (int) $actor->id,
128                message: 'Solicitação de assinatura criada.',
129            );
130
131            $this->sync->sync($request);
132
133            return $request->fresh(['signers', 'steps', 'document']);
134        });
135    }
136}