MemberService::getRefunds()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 27
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 32
rs 9.488
1
<?php
2
3
namespace Siak\Tontine\Service\Report;
4
5
use Illuminate\Database\Eloquent\Builder;
6
use Illuminate\Support\Collection;
7
use Siak\Tontine\Model\Auction;
8
use Siak\Tontine\Model\BillView;
9
use Siak\Tontine\Model\Debt;
10
use Siak\Tontine\Model\Member;
11
use Siak\Tontine\Model\Session;
12
use Siak\Tontine\Service\Payment\BalanceCalculator;
13
14
class MemberService
15
{
16
    /**
17
     * @param BalanceCalculator $balanceCalculator
18
     */
19
    public function __construct(private BalanceCalculator $balanceCalculator)
20
    {}
21
22
    /**
23
     * @param Collection $collection
24
     * @param Member|null $member
25
     *
26
     * @return Collection
27
     */
28
    private function sortByMemberName(Collection $collection, ?Member $member): Collection
29
    {
30
        return $member !== null ? $collection :
31
            $collection->sortBy('member.name', SORT_LOCALE_STRING)->values();
32
    }
33
34
    /**
35
     * @param Builder $query
36
     * @param Member $member
37
     *
38
     * @return Builder
39
     */
40
    private function hasSubscription(Builder $query, Member $member): Builder
41
    {
42
        return $query->whereHas('subscription', fn(Builder $qm) =>
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->whereHas(...ion(...) { /* ... */ }) could return the type Illuminate\Database\Query\Builder which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Builder. Consider adding an additional type-check to rule them out.
Loading history...
43
            $qm->where('member_id', $member->id));
44
    }
45
46
    /**
47
     * @param Session $session
48
     * @param Member $member|null
49
     *
50
     * @return Collection
51
     */
52
    public function getReceivables(Session $session, ?Member $member = null): Collection
53
    {
54
        return $this->sortByMemberName($session->receivables()
55
            ->when($member !== null, fn($qw) =>
56
                $this->hasSubscription($qw, $member))
0 ignored issues
show
Bug introduced by
It seems like $member can also be of type null; however, parameter $member of Siak\Tontine\Service\Rep...vice::hasSubscription() does only seem to accept Siak\Tontine\Model\Member, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

56
                $this->hasSubscription($qw, /** @scrutinizer ignore-type */ $member))
Loading history...
57
            ->with(['deposit', 'subscription.pool', 'subscription.member'])
58
            ->get()
59
            ->each(function($receivable) {
60
                $receivable->pool = $receivable->subscription->pool;
61
                $receivable->member = $receivable->subscription->member;
62
                $receivable->paid = $receivable->deposit !== null;
63
                $receivable->amount = $this->balanceCalculator->getReceivableAmount($receivable);
64
            }), $member);
65
    }
66
67
    /**
68
     * Get the deposits in the current session for receivables of different sessions.
69
     *
70
     * @param Session $session
71
     * @param Member $member|null
72
     *
73
     * @return Collection
74
     */
75
    public function getExtraDeposits(Session $session, ?Member $member = null): Collection
76
    {
77
        return $this->sortByMemberName($session->deposits()
78
            ->whereHas('receivable', fn($qr) => $qr
79
                ->where('session_id', '!=', $session->id)
80
                ->when($member !== null, fn($qm) =>
81
                    $this->hasSubscription($qm, $member)))
0 ignored issues
show
Bug introduced by
It seems like $member can also be of type null; however, parameter $member of Siak\Tontine\Service\Rep...vice::hasSubscription() does only seem to accept Siak\Tontine\Model\Member, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

81
                    $this->hasSubscription($qm, /** @scrutinizer ignore-type */ $member)))
Loading history...
82
            ->with(['receivable.session', 'receivable.subscription.pool',
83
                'receivable.subscription.member'])
84
            ->get()
85
            ->each(function($deposit) {
86
                $deposit->pool = $deposit->receivable->subscription->pool;
87
                $deposit->member = $deposit->receivable->subscription->member;
88
            }), $member);
89
    }
90
91
    /**
92
     * @param Session $session
93
     * @param Member $member|null
94
     *
95
     * @return Collection
96
     */
97
    public function getPayables(Session $session, ?Member $member = null): Collection
98
    {
99
        return $this->sortByMemberName($session->payables()
100
            ->when($member !== null, fn($qw) =>
101
                $this->hasSubscription($qw, $member))
0 ignored issues
show
Bug introduced by
It seems like $member can also be of type null; however, parameter $member of Siak\Tontine\Service\Rep...vice::hasSubscription() does only seem to accept Siak\Tontine\Model\Member, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

101
                $this->hasSubscription($qw, /** @scrutinizer ignore-type */ $member))
Loading history...
102
            ->with(['remitment', 'subscription.pool', 'subscription.member'])
103
            ->get()
104
            ->each(function($payable) use($session) {
105
                $pool = $payable->subscription->pool;
106
                $payable->pool = $pool;
107
                $payable->member = $payable->subscription->member;
108
                $payable->paid = $payable->remitment !== null;
109
                $payable->amount = $this->balanceCalculator->getPayableAmount($pool, $session);
110
            }), $member);
111
    }
112
113
    /**
114
     * @param Session $session
115
     * @param Member $member|null
116
     *
117
     * @return Collection
118
     */
119
    public function getAuctions(Session $session, ?Member $member = null): Collection
120
    {
121
        return $this->sortByMemberName($session->auctions()
122
            ->when($member !== null, fn($qm) =>
123
                $qm->whereHas('remitment', fn($qr) =>
124
                    $qr->whereHas('payable', fn($qp) =>
125
                        $this->hasSubscription($qp, $member))))
0 ignored issues
show
Bug introduced by
It seems like $member can also be of type null; however, parameter $member of Siak\Tontine\Service\Rep...vice::hasSubscription() does only seem to accept Siak\Tontine\Model\Member, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
                        $this->hasSubscription($qp, /** @scrutinizer ignore-type */ $member))))
Loading history...
126
            ->with(['remitment.payable.subscription.pool',
127
                'remitment.payable.subscription.member'])
128
            ->get()
129
            ->each(function(Auction $auction) {
130
                $subscription = $auction->remitment->payable->subscription;
131
                $auction->member = $subscription->member;
0 ignored issues
show
Bug introduced by
The property member does not seem to exist on Siak\Tontine\Model\Auction. 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
                $auction->pool = $subscription->pool;
0 ignored issues
show
Bug introduced by
The property pool does not seem to exist on Siak\Tontine\Model\Auction. 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
            ->keyBy('remitment.payable.id'), $member);
135
    }
136
137
    /**
138
     * @param Session $session
139
     * @param Member $member|null
140
     *
141
     * @return Collection
142
     */
143
    public function getBills(Session $session, ?Member $member = null): Collection
144
    {
145
        return $this->sortByMemberName(BillView::with(['round',
146
                'session', 'member', 'charge', 'bill', 'bill.settlement'])
147
            ->when($member !== null, fn(Builder $query) =>
148
                $query->where('member_id', $member->id))
149
            ->where(fn(Builder $query) => $query
150
                ->orWhere(fn(Builder $q1) => $q1->ofTypeSession($session))
151
                ->orWhere(fn(Builder $q2) => $q2->ofTypeNotSession($session)))
152
            ->get()
153
            ->each(function($bill) {
154
                $bill->paid = $bill->bill->settlement !== null;
155
                $bill->amount = $bill->bill->amount;
156
            }), $member);
157
    }
158
159
    /**
160
     * @param Session $session
161
     * @param Member $member|null
162
     *
163
     * @return Collection
164
     */
165
    public function getLoans(Session $session, ?Member $member = null): Collection
166
    {
167
        return $this->sortByMemberName($session->loans()
168
            ->when($member !== null, fn($qm) =>
169
                $qm->where('member_id', $member->id))
170
            ->with(['member', 'principal_debt.refund', 'interest_debt.refund'])
171
            ->get(), $member);
172
    }
173
174
    /**
175
     * @param Session $session
176
     * @param Member $member
177
     *
178
     * @return Collection
179
     */
180
    public function getDebts(Session $session, Member $member): Collection
181
    {
182
        return Debt::with(['loan.session', 'refund'])
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\Support\Collection. Consider adding an additional type-check to rule them out.
Loading history...
183
            // Member debts
184
            ->whereHas('loan', fn(Builder $qm) =>
185
                $qm->where('member_id', $member->id))
186
            ->where(function($query) use($session) {
187
                // Take all the debts in the current session
188
                $query->where(fn($ql) =>
189
                    $ql->whereHas('loan', fn(Builder $qs) =>
190
                        $qs->where('session_id', $session->id)));
191
                // The debts in the previous sessions.
192
                $query->orWhere(function($query) use($session) {
193
                    $query->whereHas('loan', fn(Builder $qs) =>
194
                        $qs->whereHas('session', fn($qs) => $qs->precedes($session)))
195
                    ->where(function($query) use($session) {
196
                        // The debts that are not yet refunded.
197
                        $query->orWhereDoesntHave('refund');
198
                        // The debts that are refunded in the current session.
199
                        $query->orWhereHas('refund', fn(Builder $qs) =>
200
                            $qs->where('session_id', $session->id));
201
                    });
202
                });
203
            })
204
            ->get()
205
            ->each(function($debt) {
206
                $debt->paid = $debt->refund !== null;
207
                $debt->session = $debt->loan->session;
208
            });
209
    }
210
211
    /**
212
     * @param Session $session
213
     * @param Member $member|null
214
     *
215
     * @return Collection
216
     */
217
    public function getRefunds(Session $session, ?Member $member = null): Collection
218
    {
219
        $refunds = $this->sortByMemberName($session->refunds()->select('refunds.*')
220
            ->with(['debt.loan.session', 'debt.loan.member'])
221
            // Member refunds
222
            ->when($member !== null, fn(Builder $query) => $query
223
                ->join('debts', 'refunds.debt_id', '=', 'debts.id')
224
                ->join('loans', 'debts.loan_id', '=', 'loans.id')
225
                ->where('loans.member_id', $member->id))
226
            ->get()
227
            ->each(function($refund) {
228
                $refund->amount = $refund->debt->amount;
229
                $refund->debt->session = $refund->debt->loan->session;
230
                $refund->member = $refund->debt->loan->member;
231
                $refund->is_partial = false;
232
            }), $member);
233
        $partialRefunds = $this->sortByMemberName($session->partial_refunds()
234
            ->select('partial_refunds.*')
235
            ->with(['debt.loan.session', 'debt.loan.member'])
236
            // Member refunds
237
            ->when($member !== null, fn(Builder $query) => $query
238
                ->join('debts', 'partial_refunds.debt_id', '=', 'debts.id')
239
                ->join('loans', 'debts.loan_id', '=', 'loans.id')
240
                ->where('loans.member_id', $member->id))
241
            ->get()
242
            ->each(function($refund) {
243
                $refund->debt->session = $refund->debt->loan->session;
244
                $refund->member = $refund->debt->loan->member;
245
                $refund->is_partial = true;
246
            }), $member);
247
248
        return $refunds->concat($partialRefunds);
249
    }
250
251
    /**
252
     * @param Session $session
253
     * @param Member $member|null
254
     *
255
     * @return Collection
256
     */
257
    public function getSavings(Session $session, ?Member $member = null): Collection
258
    {
259
        return $this->sortByMemberName($session->savings()
260
            ->with(['member'])
261
            ->when($member !== null, fn($query) => $query->where('member_id', $member->id))
262
            ->get(), $member);
263
    }
264
265
    /**
266
     * @param Session $session
267
     * @param Member $member|null
268
     *
269
     * @return Collection
270
     */
271
    public function getOutflows(Session $session, ?Member $member = null): Collection
272
    {
273
        return $this->sortByMemberName($session->outflows()
274
            ->with(['member', 'category'])
275
            ->when($member !== null, fn($query) => $query->where('member_id', $member->id))
276
            ->get(), $member);
277
    }
278
}
279