Passed
Push — main ( 34d123...876c9b )
by Thierry
05:11
created

RefundTrait::isEditable()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 6
rs 10
1
<?php
2
3
namespace Siak\Tontine\Service\Meeting\Credit;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Database\Eloquent\Relations\Relation;
7
use Illuminate\Support\Facades\DB;
8
use Siak\Tontine\Model\Debt;
9
use Siak\Tontine\Model\Fund;
10
use Siak\Tontine\Model\Session;
11
use Siak\Tontine\Service\Meeting\FundService;
12
use Siak\Tontine\Service\TenantService;
13
14
use function tap;
15
16
trait RefundTrait
17
{
18
    /**
19
     * @var TenantService
20
     */
21
    private TenantService $tenantService;
22
23
    /**
24
     * @var FundService
25
     */
26
    private FundService $fundService;
27
28
    /**
29
     * @param Session $session The session
30
     * @param Fund|null $fund
31
     * @param bool|null $onlyPaid
32
     * @param bool $with
33
     *
34
     * @return Builder|Relation
35
     */
36
    private function getDebtsQuery(Session $session, ?Fund $fund,
37
        ?bool $onlyPaid, bool $with): Builder|Relation
38
    {
39
        return Debt::select(['debts.*', DB::raw('members.name as member')])
40
            ->join('loans', 'debts.loan_id', '=', 'loans.id')
41
            ->join('members', 'loans.member_id', '=', 'members.id')
42
            ->whereHas('loan', function(Builder $query) use($session, $fund) {
43
                $query
44
                    ->when($fund !== null,
45
                        fn(Builder $ql) => $ql->where('fund_id', $fund->id))
46
                    ->whereIn('fund_id', DB::table('v_fund_session')
47
                        ->select('fund_id')->where('session_id', $session->id))
48
                    ->whereHas('session',
49
                        fn(Builder $qs) => $qs->where('day_date', '<=', $session->day_date));
50
            })
51
            ->where(function(Builder $query) use($session) {
52
                // The debts that are not yet refunded.
53
                $query->orWhereDoesntHave('refund');
54
                // The debts that are refunded in or after the current session.
55
                $query->orWhereHas('refund', fn(Builder $q) => $q->whereHas('session',
56
                    fn(Builder $qs) => $qs->where('day_date', '>=', $session->day_date)));
57
            })
58
            ->when($onlyPaid === false, fn(Builder $q) => $q->whereDoesntHave('refund'))
59
            ->when($onlyPaid === true, fn(Builder $q) => $q->whereHas('refund'))
60
            ->when($with, function(Builder $query) use($session) {
61
                $query
62
                    ->with([
63
                        'loan.session',
64
                        'refund.session',
65
                        'partial_refunds.session',
66
                        'partial_refund' => fn($q) => $q->where('session_id', $session->id),
67
                        'loan.fund.sessions' => fn($q) => $q->select(['id', 'day_date']),
68
                        'loan.fund.interest',
69
                    ]);
70
            });
71
    }
72
73
    /**
74
     * @param Debt $debt
75
     * @param Session $session
76
     *
77
     * @return bool
78
     */
79
    private function canCreateRefund(Debt $debt, Session $session): bool
80
    {
81
        // Already refunded
82
        // Cannot refund the principal debt in the same session as the loan.
83
        if(!$session->opened || $debt->refund !== null ||
0 ignored issues
show
Bug introduced by
The property opened does not seem to exist on Siak\Tontine\Model\Session. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
84
            $debt->is_principal && $debt->loan->session->id === $session->id)
0 ignored issues
show
Bug introduced by
The property is_principal does not seem to exist on Siak\Tontine\Model\Debt. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
85
        {
86
            return false;
87
        }
88
89
        // Cannot refund a recurrent interest debt before the principal.
90
        if($debt->is_interest && $debt->loan->recurrent_interest)
0 ignored issues
show
Bug introduced by
The property recurrent_interest does not exist on Siak\Tontine\Model\Loan. Did you mean interest?
Loading history...
Bug introduced by
The property is_interest does not seem to exist on Siak\Tontine\Model\Debt. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
91
        {
92
            return $debt->loan->principal_debt->refund !== null;
93
        }
94
95
        // Cannot refund the principal debt before the last partial refund.
96
        $lastRefund = $debt->partial_refunds->sortByDesc('session.day_date')->first();
97
        return !$lastRefund || $lastRefund->session->day_date < $session->day_date;
98
    }
99
100
    /**
101
     * @param Debt $debt
102
     * @param Session $session
103
     *
104
     * @return bool
105
     */
106
    private function canDeleteRefund(Debt $debt, Session $session): bool
107
    {
108
        // A refund can only be deleted in the same session it was created.
109
        if(!$session->opened || !$debt->refund || $debt->refund->session_id !== $session->id)
0 ignored issues
show
Bug introduced by
The property opened does not seem to exist on Siak\Tontine\Model\Session. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
110
        {
111
            return false;
112
        }
113
        // Cannot delete the principal refund if the interest is also refunded.
114
        if($debt->is_principal && $debt->loan->recurrent_interest)
0 ignored issues
show
Bug introduced by
The property is_principal does not seem to exist on Siak\Tontine\Model\Debt. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
Bug introduced by
The property recurrent_interest does not exist on Siak\Tontine\Model\Loan. Did you mean interest?
Loading history...
115
        {
116
            return $debt->loan->interest_debt->refund === null;
117
        }
118
119
        return true;
120
    }
121
122
    /**
123
     * @param Debt $debt
124
     * @param Session $session
125
     *
126
     * @return bool
127
     */
128
    private function canCreatePartialRefund(Debt $debt, Session $session): bool
129
    {
130
        // Cannot refund the principal debt in the same session.
131
        if(!$session->opened || $debt->refund !== null ||
0 ignored issues
show
Bug introduced by
The property opened does not seem to exist on Siak\Tontine\Model\Session. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
132
            ($debt->is_principal && $debt->loan->session->id === $session->id))
0 ignored issues
show
Bug introduced by
The property is_principal does not seem to exist on Siak\Tontine\Model\Debt. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
133
        {
134
            return false;
135
        }
136
137
        return true;
138
    }
139
140
    /**
141
     * @param Debt $debt
142
     * @param Session $session
143
     *
144
     * @return bool
145
     */
146
    private function isEditable(Debt $debt, Session $session): bool
147
    {
148
        return $debt->loan->session_id === $session->id ? false :
149
            ($debt->refund !== null ?
150
                $this->canDeleteRefund($debt, $session) :
151
                $this->canCreateRefund($debt, $session));
152
    }
153
154
    /**
155
     * @param Debt $debt
156
     * @param Session $session
157
     *
158
     * @return void
159
     */
160
    private function fillDebt(Debt $debt, Session $session): void
161
    {
162
        $debt->isEditable = $this->isEditable($debt, $session);
0 ignored issues
show
Bug introduced by
The property isEditable does not seem to exist on Siak\Tontine\Model\Debt. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
163
        $debt->canPartiallyRefund = $this->canCreatePartialRefund($debt, $session);
0 ignored issues
show
Bug introduced by
The property canPartiallyRefund does not seem to exist on Siak\Tontine\Model\Debt. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
164
    }
165
166
    /**
167
     * @param int $debtId
168
     *
169
     * @return Debt|null
170
     */
171
    public function getDebt(int $debtId): ?Debt
172
    {
173
        return Debt::whereHas('loan',
174
            fn(Builder|Relation $loanQuery) => $loanQuery->whereHas('member',
175
                fn(Builder|Relation $memberQuery) => $memberQuery->where('guild_id',
176
                    $this->tenantService->guild()->id)))
177
            ->find($debtId);
178
    }
179
180
    /**
181
     * @param Session $session The session
182
     * @param Fund|null $fund
183
     * @param int $debtId
184
     *
185
     * @return Debt|null
186
     */
187
    public function getFundDebt(Session $session, ?Fund $fund, int $debtId): ?Debt
188
    {
189
        return tap($this->getDebtsQuery($session, $fund, null, true)->find($debtId),
190
            fn(?Debt $debt) => $debt !== null && $this->fillDebt($debt, $session));
191
    }
192
}
193