Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.06% covered (success)
97.06%
33 / 34
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
GenerateReceiptAction
97.06% covered (success)
97.06%
33 / 34
50.00% covered (danger)
50.00%
1 / 2
6
0.00% covered (danger)
0.00%
0 / 1
 execute
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
3
 nextNumber
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
3.01
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Financial\Application\Actions;
6
7use Illuminate\Support\Facades\DB;
8use ImovelZapAi\Financial\Domain\Events\ReceiptGenerated;
9use ImovelZapAi\Financial\Domain\Exceptions\FinancialException;
10use ImovelZapAi\Financial\Infrastructure\Persistence\Eloquent\FinancialPaymentModel;
11use ImovelZapAi\Financial\Infrastructure\Persistence\Eloquent\FinancialReceiptModel;
12
13final class GenerateReceiptAction
14{
15    public function execute(string $tenantId, string $paymentId, ?string $notes = null): FinancialReceiptModel
16    {
17        return DB::transaction(function () use ($tenantId, $paymentId, $notes): FinancialReceiptModel {
18            $payment = FinancialPaymentModel::query()
19                ->where('tenant_id', $tenantId)
20                ->find($paymentId);
21
22            if ($payment === null) {
23                throw FinancialException::notFound('Pagamento');
24            }
25
26            $existing = FinancialReceiptModel::query()
27                ->where('tenant_id', $tenantId)
28                ->where('payment_id', $paymentId)
29                ->first();
30
31            if ($existing !== null) {
32                return $existing;
33            }
34
35            $number = $this->nextNumber($tenantId);
36
37            $receipt = FinancialReceiptModel::query()->create([
38                'tenant_id' => $tenantId,
39                'deal_id' => $payment->deal_id,
40                'payment_id' => $payment->id,
41                'number' => $number,
42                'issued_at' => now(),
43                'notes' => $notes,
44            ]);
45
46            ReceiptGenerated::dispatch($tenantId, (string) $receipt->id);
47
48            return $receipt;
49        });
50    }
51
52    private function nextNumber(string $tenantId): string
53    {
54        $prefix = 'FIN-'.now()->format('Ymd').'-';
55        $last = FinancialReceiptModel::query()
56            ->where('tenant_id', $tenantId)
57            ->where('number', 'like', $prefix.'%')
58            ->orderByDesc('number')
59            ->value('number');
60
61        $seq = 1;
62        if (is_string($last) && preg_match('/-(\d{4})$/', $last, $matches)) {
63            $seq = ((int) $matches[1]) + 1;
64        }
65
66        return $prefix.str_pad((string) $seq, 4, '0', STR_PAD_LEFT);
67    }
68}