Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.43% covered (success)
91.43%
32 / 35
33.33% covered (danger)
33.33%
1 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
NotifiesSignersViaWhatsapp
91.43% covered (success)
91.43%
32 / 35
33.33% covered (danger)
33.33%
1 / 3
12.09
0.00% covered (danger)
0.00%
0 / 1
 outboundMessenger
n/a
0 / 0
n/a
0 / 0
0
 actableSigners
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
3.01
 notifySigners
90.48% covered (success)
90.48%
19 / 21
0.00% covered (danger)
0.00%
0 / 1
7.04
 resolveSignerContact
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Signatures\Infrastructure\Providers\Drivers\Concerns;
6
7use ImovelZapAi\Conversations\Application\Actions\SendOutboundMessageAction;
8use ImovelZapAi\Conversations\Infrastructure\Persistence\Eloquent\ContactModel;
9use ImovelZapAi\Signatures\Domain\Enums\SignatureMode;
10use ImovelZapAi\Signatures\Domain\Enums\SignerStatus;
11use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignatureRequestModel;
12use ImovelZapAi\Signatures\Infrastructure\Persistence\Eloquent\SignerModel;
13use Illuminate\Support\Collection;
14
15trait NotifiesSignersViaWhatsapp
16{
17    abstract protected function outboundMessenger(): SendOutboundMessageAction;
18
19    /** @return Collection<int, SignerModel> */
20    protected function actableSigners(SignatureRequestModel $request): Collection
21    {
22        $request->loadMissing('signers');
23
24        $pending = $request->signers->filter(
25            fn (SignerModel $signer): bool => $signer->status === SignerStatus::Pending,
26        );
27
28        if ($request->mode === SignatureMode::Parallel) {
29            return $pending->values();
30        }
31
32        $minOrder = $pending->min('order');
33        if ($minOrder === null) {
34            return collect();
35        }
36
37        return $pending->where('order', $minOrder)->values();
38    }
39
40    protected function notifySigners(SignatureRequestModel $request): void
41    {
42        $request->loadMissing(['deal.conversation', 'document.contact']);
43        $signerUrls = (array) ($request->meta['signer_urls'] ?? []);
44        $conversation = $request->deal?->conversation;
45
46        foreach ($this->actableSigners($request) as $signer) {
47            $signer->forceFill(['status' => SignerStatus::Notified])->save();
48
49            $url = $signerUrls[$signer->id] ?? null;
50            if ($url === null) {
51                continue;
52            }
53
54            $contact = $this->resolveSignerContact($signer, $request);
55            if ($contact === null) {
56                continue;
57            }
58
59            try {
60                $name = $signer->name ?: ($contact->display_name ?: 'olá');
61                $title = $request->title;
62                $body = "Olá {$name}! Você precisa assinar o documento \"{$title}\". Acesse pelo link seguro:\n{$url}";
63
64                $this->outboundMessenger()->execute(
65                    $request->tenant_id,
66                    $contact,
67                    $body,
68                    $conversation,
69                );
70            } catch (\Throwable) {
71                // Notificação falhou; link permanece válido.
72            }
73        }
74    }
75
76    private function resolveSignerContact(SignerModel $signer, SignatureRequestModel $request): ?ContactModel
77    {
78        if ($signer->contact_id !== null) {
79            $signer->loadMissing('contact');
80
81            return $signer->contact;
82        }
83
84        return $request->document?->contact;
85    }
86}