Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
94.74% covered (success)
94.74%
36 / 38
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
RecordSignatureViewAction
94.74% covered (success)
94.74%
36 / 38
50.00% covered (danger)
50.00%
1 / 2
6.01
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
 execute
94.59% covered (success)
94.59%
35 / 37
0.00% covered (danger)
0.00%
0 / 1
5.00
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Signatures\Application\Actions;
6
7use ImovelZapAi\Signatures\Application\Services\SignatureAccessTokenService;
8use ImovelZapAi\Signatures\Application\Services\SignatureAuditWriter;
9use ImovelZapAi\Signatures\Application\Services\SignatureStatusSynchronizer;
10use ImovelZapAi\Signatures\Domain\Enums\SignatureAuditEvent;
11use ImovelZapAi\Signatures\Domain\Enums\SignerStatus;
12use ImovelZapAi\Signatures\Domain\Events\SignatureViewed;
13use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignatureAuditModel;
14use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignatureRequestModel;
15use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignatureStepModel;
16use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignerModel;
17
18final class RecordSignatureViewAction
19{
20    public function __construct(
21        private readonly SignatureAccessTokenService $tokens,
22        private readonly SignatureAuditWriter $audit,
23        private readonly SignatureStatusSynchronizer $sync,
24    ) {}
25
26    /** @return array{request: SignatureRequestModel, signer: SignerModel} */
27    public function execute(string $plainToken, ?string $ip = null, ?string $userAgent = null): array
28    {
29        $resolved = $this->tokens->resolve($plainToken);
30        $signer = $resolved['signer'];
31        $request = $resolved['request'];
32
33        if (in_array($signer->status, [SignerStatus::Pending, SignerStatus::Notified], true)) {
34            $signer->forceFill([
35                'status' => SignerStatus::Viewed,
36                'viewed_at' => now(),
37            ])->save();
38        } elseif ($signer->viewed_at === null) {
39            $signer->forceFill(['viewed_at' => now()])->save();
40        }
41
42        SignatureStepModel::query()
43            ->where('signature_request_id', $request->id)
44            ->where('order_index', $signer->order)
45            ->whereNull('opened_at')
46            ->update(['opened_at' => now(), 'status' => 'opened']);
47
48        $recent = SignatureAuditModel::query()
49            ->where('signature_request_id', $request->id)
50            ->where('signer_id', $signer->id)
51            ->where('event', SignatureAuditEvent::Viewed)
52            ->where('created_at', '>=', now()->subHour())
53            ->exists();
54
55        if (! $recent) {
56            $this->audit->write(
57                $request->tenant_id,
58                $request->id,
59                SignatureAuditEvent::Viewed,
60                actorType: 'signer',
61                signerId: $signer->id,
62                payload: ['ip' => $ip, 'user_agent' => $userAgent ? substr($userAgent, 0, 512) : null],
63                message: 'Signatário visualizou o documento.',
64            );
65            SignatureViewed::dispatch($request->tenant_id, $request->id);
66        }
67
68        $this->sync->sync($request->fresh(['signers']));
69
70        return [
71            'request' => $request->fresh(['signers', 'steps', 'document']),
72            'signer' => $signer->fresh(),
73        ];
74    }
75}