Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
62 / 62
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
CalculateCommissionAction
100.00% covered (success)
100.00%
62 / 62
100.00% covered (success)
100.00%
2 / 2
8
100.00% covered (success)
100.00%
1 / 1
 execute
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
3
 resolveSplits
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
1 / 1
5
1<?php
2
3declare(strict_types=1);
4
5namespace ImovelZapAi\Financial\Application\Actions;
6
7use Illuminate\Support\Facades\DB;
8use ImovelZapAi\Financial\Domain\Enums\BeneficiaryType;
9use ImovelZapAi\Financial\Domain\Enums\CommissionBasis;
10use ImovelZapAi\Financial\Domain\Enums\CommissionStatus;
11use ImovelZapAi\Financial\Domain\Enums\SplitStatus;
12use ImovelZapAi\Financial\Domain\Events\CommissionCalculated;
13use ImovelZapAi\Financial\Infrastructure\Persistence\Eloquent\FinancialCommissionModel;
14use ImovelZapAi\Financial\Infrastructure\Persistence\Eloquent\FinancialCommissionRuleModel;
15use ImovelZapAi\Financial\Infrastructure\Persistence\Eloquent\FinancialCommissionSplitModel;
16use ImovelZapAi\Sales\Infrastructure\Persistence\Eloquent\DealModel;
17
18final class CalculateCommissionAction
19{
20    public function execute(
21        string $tenantId,
22        DealModel $deal,
23        FinancialCommissionRuleModel $rule,
24    ): FinancialCommissionModel {
25        return DB::transaction(function () use ($tenantId, $deal, $rule): FinancialCommissionModel {
26            $baseAmount = (float) ($deal->amount ?? 0);
27            $totalAmount = $rule->basis === CommissionBasis::Percent
28                ? round($baseAmount * ((float) $rule->value / 100), 2)
29                : round((float) $rule->value, 2);
30
31            $commission = FinancialCommissionModel::query()->create([
32                'tenant_id' => $tenantId,
33                'deal_id' => $deal->id,
34                'rule_id' => $rule->id,
35                'base_amount' => $baseAmount,
36                'total_amount' => $totalAmount,
37                'status' => CommissionStatus::Calculated,
38                'calculated_at' => now(),
39            ]);
40
41            $splitDefinitions = $this->resolveSplits($deal, $rule);
42
43            foreach ($splitDefinitions as $split) {
44                $amount = round($totalAmount * ($split['percent'] / 100), 2);
45                FinancialCommissionSplitModel::query()->create([
46                    'tenant_id' => $tenantId,
47                    'commission_id' => $commission->id,
48                    'beneficiary_type' => $split['type'],
49                    'beneficiary_user_id' => $split['user_id'] ?? null,
50                    'beneficiary_label' => $split['label'],
51                    'percent' => $split['percent'],
52                    'amount' => $amount,
53                    'status' => SplitStatus::Pending,
54                ]);
55            }
56
57            CommissionCalculated::dispatch($tenantId, (string) $commission->id);
58
59            return $commission->load('splits');
60        });
61    }
62
63    /**
64     * @return list<array{type: BeneficiaryType, user_id?: int|null, label: string, percent: float}>
65     */
66    private function resolveSplits(DealModel $deal, FinancialCommissionRuleModel $rule): array
67    {
68        if (is_array($rule->split_json) && $rule->split_json !== []) {
69            $splits = [];
70            foreach ($rule->split_json as $item) {
71                $type = BeneficiaryType::tryFrom((string) ($item['type'] ?? 'agency')) ?? BeneficiaryType::Agency;
72                $splits[] = [
73                    'type' => $type,
74                    'user_id' => $item['user_id'] ?? null,
75                    'label' => (string) ($item['label'] ?? $type->label()),
76                    'percent' => (float) ($item['percent'] ?? 0),
77                ];
78            }
79
80            return $splits;
81        }
82
83        if ($deal->responsible_user_id !== null) {
84            return [
85                [
86                    'type' => BeneficiaryType::Agency,
87                    'label' => 'Imobiliária',
88                    'percent' => 50.0,
89                ],
90                [
91                    'type' => BeneficiaryType::User,
92                    'user_id' => $deal->responsible_user_id,
93                    'label' => 'Corretor responsável',
94                    'percent' => 50.0,
95                ],
96            ];
97        }
98
99        return [
100            [
101                'type' => BeneficiaryType::Agency,
102                'label' => 'Imobiliária',
103                'percent' => 100.0,
104            ],
105        ];
106    }
107}