RefundTrait::canDeleteRefund()   A
last analyzed

Complexity

Conditions 6
Paths 3

Size

Total Lines 14
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 5
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 14
rs 9.2222
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\Saving\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
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('member_defs.name as member')])
0 ignored issues
show
Bug Best Practice introduced by
The expression return Siak\Tontine\Mode...ion(...) { /* ... */ }) could return the type Illuminate\Database\Eloq...gHasThroughRelationship which is incompatible with the type-hinted return Illuminate\Database\Eloq...uent\Relations\Relation. Consider adding an additional type-check to rule them out.
Loading history...
40
            ->join('loans', 'debts.loan_id', '=', 'loans.id')
41
            ->join('members', 'loans.member_id', '=', 'members.id')
42
            ->join('member_defs', 'members.def_id', '=', 'member_defs.id')
43
            ->join('sessions', 'loans.session_id', '=', 'sessions.id')
44
            ->when($fund !== null, fn(Builder $ql) => $ql->where('loans.fund_id', $fund->id))
45
            ->whereIn('loans.fund_id', DB::table('v_fund_session')
46
                ->select('fund_id')->where('session_id', $session->id))
47
            ->where('sessions.day_date', '<=', $session->day_date)
48
            ->where(fn(Builder $query) => $query
49
                // The debts that are not yet refunded.
50
                ->orWhereDoesntHave('refund')
51
                // The debts that are refunded in or after the current session.
52
                ->orWhereHas('refund', fn(Builder $q) => $q->whereHas('session',
53
                    fn(Builder $qs) => $qs->where('day_date', '>=', $session->day_date))))
54
            ->when($onlyPaid === false, fn(Builder $q) => $q->whereDoesntHave('refund'))
55
            ->when($onlyPaid === true, fn(Builder $q) => $q->whereHas('refund'))
56
            ->when($with, fn(Builder $query) => $query->with([
57
                'loan.session',
58
                'refund.session',
59
                'partial_refunds.session',
60
                'partial_refund' => fn($q) => $q->where('session_id', $session->id),
61
                'loan.fund.sessions' => fn($q) => $q->select(['id', 'day_date']),
62
                'loan.fund.interest',
63
            ]));
64
    }
65
66
    /**
67
     * @param Debt $debt
68
     * @param Session $session
69
     *
70
     * @return bool
71
     */
72
    private function canCreateRefund(Debt $debt, Session $session): bool
73
    {
74
        // Already refunded
75
        // Cannot refund the principal debt in the same session as the loan.
76
        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...
77
            ($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...
78
        {
79
            return false;
80
        }
81
82
        // Cannot refund a recurrent interest debt before the principal.
83
        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...
84
        {
85
            $refund = $debt->loan->principal_debt->refund;
86
            return $refund !== null && $refund->session->day_date <= $session->day_date;
87
        }
88
89
        // Cannot refund a debt before the last partial refund.
90
        $lastRefund = $debt->partial_refunds->sortByDesc('session.day_date')->first();
91
        return !$lastRefund || $lastRefund->session->day_date < $session->day_date;
92
    }
93
94
    /**
95
     * @param Debt $debt
96
     * @param Session $session
97
     *
98
     * @return bool
99
     */
100
    private function canDeleteRefund(Debt $debt, Session $session): bool
101
    {
102
        // A refund can only be deleted in the same session it was created.
103
        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...
104
        {
105
            return false;
106
        }
107
        // Cannot delete the principal refund if the interest is also refunded.
108
        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...
109
        {
110
            return $debt->loan->interest_debt->refund === null;
111
        }
112
113
        return true;
114
    }
115
116
    /**
117
     * @param Debt $debt
118
     * @param Session $session
119
     *
120
     * @return bool
121
     */
122
    private function canCreatePartialRefund(Debt $debt, Session $session): bool
123
    {
124
        // Cannot refund the principal debt in the same session.
125
        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...
126
            ($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...
127
        {
128
            return false;
129
        }
130
131
        return true;
132
    }
133
134
    /**
135
     * @param Debt $debt
136
     * @param Session $session
137
     *
138
     * @return bool
139
     */
140
    private function isEditable(Debt $debt, Session $session): bool
141
    {
142
        return $debt->refund !== null ?
143
            $this->canDeleteRefund($debt, $session) :
144
            $this->canCreateRefund($debt, $session);
145
    }
146
147
    /**
148
     * @param Debt $debt
149
     * @param Session $session
150
     *
151
     * @return void
152
     */
153
    private function fillDebt(Debt $debt, Session $session): void
154
    {
155
        $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...
156
        $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...
157
    }
158
159
    /**
160
     * @param Session $session
161
     * @param int $debtId
162
     *
163
     * @return Debt|null
164
     */
165
    public function getDebt(Session $session, int $debtId): ?Debt
166
    {
167
        return Debt::whereHas('loan', fn($ql) =>
168
                $ql->whereIn('fund_id', $session->funds()->pluck('id')))
169
            ->find($debtId);
170
    }
171
172
    /**
173
     * @param Session $session
174
     * @param Fund|null $fund
175
     * @param int $debtId
176
     *
177
     * @return Debt|null
178
     */
179
    public function getFundDebt(Session $session, ?Fund $fund, int $debtId): ?Debt
180
    {
181
        return tap($this->getDebtsQuery($session, $fund, null, true)->find($debtId),
182
            fn(?Debt $debt) => $debt !== null && $this->fillDebt($debt, $session));
183
    }
184
}
185