Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
88.16% covered (warning)
88.16%
67 / 76
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
DecideDocumentAction
88.16% covered (warning)
88.16%
67 / 76
50.00% covered (danger)
50.00%
1 / 2
19.60
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
88.00% covered (warning)
88.00%
66 / 75
0.00% covered (danger)
0.00%
0 / 1
18.56
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Documents\Application\Actions;
6
7use App\Models\User;
8use Illuminate\Support\Facades\DB;
9use ImovelZapAi\Documents\Application\Services\DocumentHistoryWriter;
10use ImovelZapAi\Documents\Application\Services\DocumentShareTokenService;
11use ImovelZapAi\Documents\Domain\Enums\DocumentDecisionType;
12use ImovelZapAi\Documents\Domain\Enums\DocumentHistoryEvent;
13use ImovelZapAi\Documents\Domain\Enums\DocumentStatus;
14use ImovelZapAi\Documents\Domain\Events\DocumentAccepted;
15use ImovelZapAi\Documents\Domain\Events\DocumentRejected;
16use ImovelZapAi\Documents\Domain\Exceptions\DocumentException;
17use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\BusinessDocumentModel;
18use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\DocumentCommentModel;
19use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\DocumentDecisionModel;
20use ImovelZapAi\Sales\Application\Actions\ChangeDealStageAction;
21use ImovelZapAi\Sales\Domain\Enums\DealLostReason;
22use ImovelZapAi\Sales\Domain\Enums\DealStageKey;
23
24final class DecideDocumentAction
25{
26    public function __construct(
27        private readonly DocumentShareTokenService $tokens,
28        private readonly DocumentHistoryWriter $history,
29        private readonly ChangeDealStageAction $changeDealStage,
30    ) {}
31
32    public function execute(
33        string $plainToken,
34        string $decision,
35        ?string $comment = null,
36        ?string $decidedByName = null,
37        ?string $ip = null,
38    ): BusinessDocumentModel {
39        $decisionEnum = DocumentDecisionType::tryFrom($decision)
40            ?? throw DocumentException::invalidState('decisão inválida.');
41
42        $resolved = $this->tokens->resolve($plainToken);
43        $document = $resolved['document'];
44
45        if (! $document->typeAcceptsDecision()) {
46            throw DocumentException::invalidState('este tipo de documento não aceita decisão do cliente.');
47        }
48
49        return DB::transaction(function () use ($document, $decisionEnum, $comment, $decidedByName, $ip): BusinessDocumentModel {
50            if ($document->current_version_id === null) {
51                throw DocumentException::invalidState('sem versão.');
52            }
53
54            DocumentDecisionModel::query()->create([
55                'tenant_id' => $document->tenant_id,
56                'business_document_id' => $document->id,
57                'document_version_id' => $document->current_version_id,
58                'decision' => $decisionEnum,
59                'comment' => $comment,
60                'decided_by_name' => $decidedByName ?? $document->contact?->display_name,
61                'ip' => $ip,
62                'created_at' => now(),
63            ]);
64
65            if ($comment !== null && trim($comment) !== '') {
66                DocumentCommentModel::query()->create([
67                    'tenant_id' => $document->tenant_id,
68                    'business_document_id' => $document->id,
69                    'document_version_id' => $document->current_version_id,
70                    'author_type' => 'client',
71                    'author_name' => $decidedByName ?? $document->contact?->display_name,
72                    'body' => $comment,
73                    'visible_to_client' => true,
74                ]);
75                $this->history->write(
76                    $document->tenant_id,
77                    $document->id,
78                    DocumentHistoryEvent::CommentAdded,
79                    actorType: 'client',
80                    message: 'Comentário do cliente no documento.',
81                );
82            }
83
84            $status = match ($decisionEnum) {
85                DocumentDecisionType::Accepted => DocumentStatus::Accepted,
86                DocumentDecisionType::Rejected => DocumentStatus::Rejected,
87                DocumentDecisionType::ChangesRequested => DocumentStatus::ChangesRequested,
88            };
89
90            $document->forceFill([
91                'status' => $status,
92                'decided_at' => $decisionEnum === DocumentDecisionType::ChangesRequested ? null : now(),
93            ])->save();
94
95            $deal = $document->deal;
96            $systemUser = User::query()->where('tenant_id', $document->tenant_id)->orderBy('id')->first();
97
98            if ($deal !== null && $systemUser !== null && $deal->closed_at === null) {
99                try {
100                    match ($decisionEnum) {
101                        DocumentDecisionType::Accepted => $this->changeDealStage->execute(
102                            $deal,
103                            $systemUser,
104                            DealStageKey::Documentation->value,
105                        ),
106                        DocumentDecisionType::Rejected => $this->changeDealStage->execute(
107                            $deal,
108                            $systemUser,
109                            DealStageKey::Lost->value,
110                            DealLostReason::Other->value,
111                            $comment ?? 'Documento recusado',
112                        ),
113                        DocumentDecisionType::ChangesRequested => $this->changeDealStage->execute(
114                            $deal,
115                            $systemUser,
116                            DealStageKey::Negotiation->value,
117                        ),
118                    };
119                } catch (\Throwable) {
120                }
121            }
122
123            if ($decisionEnum === DocumentDecisionType::Accepted) {
124                $this->history->write($document->tenant_id, $document->id, DocumentHistoryEvent::Accepted, actorType: 'client', message: 'Documento aceito.');
125                DocumentAccepted::dispatch($document->tenant_id, $document->id);
126            } elseif ($decisionEnum === DocumentDecisionType::Rejected) {
127                $this->history->write($document->tenant_id, $document->id, DocumentHistoryEvent::Rejected, actorType: 'client', message: 'Documento recusado.');
128                DocumentRejected::dispatch($document->tenant_id, $document->id);
129            } else {
130                $this->history->write($document->tenant_id, $document->id, DocumentHistoryEvent::ChangesRequested, actorType: 'client', message: 'Cliente solicitou alterações.');
131            }
132
133            return $document->fresh(['currentVersion', 'decisions', 'comments']);
134        });
135    }
136}