Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
95.35% |
41 / 43 |
|
66.67% |
2 / 3 |
CRAP | |
0.00% |
0 / 1 |
| ShareDocumentAction | |
95.35% |
41 / 43 |
|
66.67% |
2 / 3 |
11 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| execute | |
95.12% |
39 / 41 |
|
0.00% |
0 / 1 |
9 | |||
| isProposalType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace ImovelZapAi\Documents\Application\Actions; |
| 6 | |
| 7 | use App\Models\User; |
| 8 | use Illuminate\Support\Facades\DB; |
| 9 | use ImovelZapAi\Conversations\Application\Actions\SendOutboundMessageAction; |
| 10 | use ImovelZapAi\Documents\Application\Services\DocumentHistoryWriter; |
| 11 | use ImovelZapAi\Documents\Application\Services\DocumentPdfGenerator; |
| 12 | use ImovelZapAi\Documents\Application\Services\DocumentShareTokenService; |
| 13 | use ImovelZapAi\Documents\Domain\Enums\DocumentHistoryEvent; |
| 14 | use ImovelZapAi\Documents\Domain\Enums\DocumentStatus; |
| 15 | use ImovelZapAi\Documents\Domain\Events\DocumentShared; |
| 16 | use ImovelZapAi\Documents\Domain\Exceptions\DocumentException; |
| 17 | use ImovelZapAi\Documents\Infrastructure\Persistence\Eloquent\BusinessDocumentModel; |
| 18 | use ImovelZapAi\Sales\Application\Actions\ChangeDealStageAction; |
| 19 | use ImovelZapAi\Sales\Domain\Enums\DealStageKey; |
| 20 | use ImovelZapAi\Documents\Domain\Enums\DocumentType; |
| 21 | |
| 22 | final class ShareDocumentAction |
| 23 | { |
| 24 | public function __construct( |
| 25 | private readonly DocumentShareTokenService $tokens, |
| 26 | private readonly DocumentPdfGenerator $pdf, |
| 27 | private readonly DocumentHistoryWriter $history, |
| 28 | private readonly SendOutboundMessageAction $sendText, |
| 29 | private readonly ChangeDealStageAction $changeDealStage, |
| 30 | ) {} |
| 31 | |
| 32 | /** @return array{url: string, plain_token: string} */ |
| 33 | public function execute(BusinessDocumentModel $document, User $actor, bool $sendWhatsapp = true): array |
| 34 | { |
| 35 | if ($document->current_version_id === null) { |
| 36 | throw DocumentException::invalidState('documento sem versão.'); |
| 37 | } |
| 38 | |
| 39 | $result = DB::transaction(function () use ($document, $actor): array { |
| 40 | $document->loadMissing(['contact', 'currentVersion', 'deal']); |
| 41 | $issued = $this->tokens->issue($document, $document->current_version_id, $document->expires_at); |
| 42 | |
| 43 | $pdf = $this->pdf->generate($document, $document->currentVersion, $issued['url']); |
| 44 | $document->currentVersion->forceFill([ |
| 45 | 'pdf_disk' => $pdf['disk'], |
| 46 | 'pdf_path' => $pdf['path'], |
| 47 | ])->save(); |
| 48 | |
| 49 | $document->forceFill([ |
| 50 | 'status' => DocumentStatus::Shared, |
| 51 | 'shared_at' => now(), |
| 52 | ])->save(); |
| 53 | |
| 54 | if ($document->deal !== null && $document->deal->closed_at === null && $this->isProposalType($document->document_type)) { |
| 55 | try { |
| 56 | $this->changeDealStage->execute($document->deal, $actor, DealStageKey::Proposal->value); |
| 57 | } catch (\Throwable) { |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | $this->history->write( |
| 62 | $document->tenant_id, |
| 63 | $document->id, |
| 64 | DocumentHistoryEvent::Shared, |
| 65 | actorType: 'broker', |
| 66 | actorUserId: (int) $actor->id, |
| 67 | payload: ['url' => $issued['url']], |
| 68 | message: 'Documento compartilhado no portal.', |
| 69 | ); |
| 70 | |
| 71 | DocumentShared::dispatch($document->tenant_id, $document->id); |
| 72 | |
| 73 | return ['url' => $issued['url'], 'plain_token' => $issued['plain']]; |
| 74 | }); |
| 75 | |
| 76 | if ($sendWhatsapp) { |
| 77 | try { |
| 78 | $document->loadMissing(['contact', 'deal.conversation']); |
| 79 | $name = $document->contact?->display_name ?: 'olá'; |
| 80 | $body = "Olá {$name}! Seu documento está pronto. Acesse pelo link seguro:\n{$result['url']}\n\nSem anexos — tudo no portal."; |
| 81 | $this->sendText->execute( |
| 82 | $document->tenant_id, |
| 83 | $document->contact, |
| 84 | $body, |
| 85 | $document->deal?->conversation, |
| 86 | ); |
| 87 | } catch (\Throwable) { |
| 88 | // Link já publicado; falha de WhatsApp não bloqueia o compartilhamento. |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | return $result; |
| 93 | } |
| 94 | |
| 95 | private function isProposalType(DocumentType $type): bool |
| 96 | { |
| 97 | return in_array($type, [DocumentType::ProposalSale, DocumentType::ProposalRent], true); |
| 98 | } |
| 99 | } |