Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
1 / 1
CRAP
100.00% covered (success)
100.00%
1 / 1
GetProposalDashboardQuery
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
1 / 1
 execute
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Proposals\Application\Queries;
6
7use ImovelZapAi\Proposals\Domain\Enums\ProposalStatus;
8use ImovelZapAi\Proposals\Infrastructure\Persistence\Eloquent\ProposalModel;
9
10final class GetProposalDashboardQuery
11{
12    /** @return array<string, mixed> */
13    public function execute(string $tenantId): array
14    {
15        $sent = ProposalModel::query()->where('tenant_id', $tenantId)->whereNotNull('shared_at')->count();
16        $open = ProposalModel::query()->where('tenant_id', $tenantId)->whereIn('status', [
17            ProposalStatus::Shared->value,
18            ProposalStatus::ChangesRequested->value,
19        ])->count();
20        $accepted = ProposalModel::query()->where('tenant_id', $tenantId)->where('status', ProposalStatus::Accepted)->count();
21        $rejected = ProposalModel::query()->where('tenant_id', $tenantId)->where('status', ProposalStatus::Rejected)->count();
22        $expired = ProposalModel::query()->where('tenant_id', $tenantId)->where(function ($q): void {
23            $q->where('status', ProposalStatus::Expired)
24                ->orWhere(function ($q2): void {
25                    $q2->whereIn('status', [ProposalStatus::Shared->value, ProposalStatus::Draft->value])
26                        ->whereNotNull('expires_at')
27                        ->where('expires_at', '<', now());
28                });
29        })->count();
30
31        $decided = $accepted + $rejected;
32        $acceptRate = $decided > 0 ? round(($accepted / $decided) * 100, 1) : null;
33
34        $avgHours = ProposalModel::query()
35            ->where('tenant_id', $tenantId)
36            ->where('status', ProposalStatus::Accepted)
37            ->whereNotNull('shared_at')
38            ->whereNotNull('decided_at')
39            ->get(['shared_at', 'decided_at'])
40            ->avg(fn (ProposalModel $p) => $p->shared_at->diffInMinutes($p->decided_at) / 60);
41
42        $totalAmount = ProposalModel::query()
43            ->where('tenant_id', $tenantId)
44            ->whereIn('status', [ProposalStatus::Shared->value, ProposalStatus::Accepted->value, ProposalStatus::ChangesRequested->value])
45            ->sum('amount');
46
47        return [
48            'sent' => $sent,
49            'open' => $open,
50            'accepted' => $accepted,
51            'accept_rate' => $acceptRate,
52            'avg_accept_hours' => $avgHours !== null ? round((float) $avgHours, 1) : null,
53            'expired' => $expired,
54            'total_amount' => (float) $totalAmount,
55        ];
56    }
57}