Passed
Push — main ( 1c458a...5a987a )
by Thierry
10:56 queued 05:26
created

SessionService::getTotalCharges()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 32
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 5
eloc 23
c 4
b 0
f 0
nc 2
nop 2
dl 0
loc 32
rs 9.2408
1
<?php
2
3
namespace Siak\Tontine\Service\Report;
4
5
use Closure;
6
use Illuminate\Database\Query\Builder;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Facades\DB;
9
use Siak\Tontine\Model\Debt;
10
use Siak\Tontine\Model\Member;
11
use Siak\Tontine\Model\Outflow;
12
use Siak\Tontine\Model\Session;
13
use Siak\Tontine\Service\Payment\BalanceCalculator;
14
15
class SessionService
16
{
17
    use Traits\Queries;
18
19
    /**
20
     * @param BalanceCalculator $balanceCalculator
21
     */
22
    public function __construct(private BalanceCalculator $balanceCalculator)
23
    {}
24
25
    /**
26
     * @param Session $session
27
     *
28
     * @return Collection
29
     */
30
    public function getReceivables(Session $session): Collection
31
    {
32
        return $session->pools()
33
            ->withCount([
34
                'subscriptions as total_count' => function($query) use($session) {
35
                    $query->whereHas('receivables', function($query) use($session) {
36
                        $query->where('session_id', $session->id);
37
                    });
38
                },
39
                'subscriptions as paid_count' => function($query) use($session) {
40
                    $query->whereHas('receivables', function($query) use($session) {
41
                        $query->where('session_id', $session->id)->whereHas('deposit');
42
                    });
43
                },
44
            ])
45
            ->get()
46
            ->each(function($pool) use($session) {
47
                $pool->total_amount = $pool->amount * $pool->total_count;
48
                $pool->paid_amount = $this->balanceCalculator->getPoolDepositAmount($pool, $session);
49
            });
50
    }
51
52
    /**
53
     * @param Session $session
54
     *
55
     * @return Collection
56
     */
57
    public function getPayables(Session $session): Collection
58
    {
59
        return $session->pools()
60
            ->withCount([
61
                'sessions',
62
                'subscriptions as total_count' => function($query) use($session) {
63
                    $query->whereHas('payable', function($query) use($session) {
64
                        $query->where('session_id', $session->id);
65
                    });
66
                },
67
                'subscriptions as paid_count' => function($query) use($session) {
68
                    $query->whereHas('payable', function($query) use($session) {
69
                        $query->where('session_id', $session->id)->whereHas('remitment');
70
                    });
71
                },
72
            ])
73
            ->get()
74
            ->each(function($pool) use($session) {
75
                $pool->total_amount = $pool->amount * $pool->total_count * $pool->sessions_count;
76
                $pool->paid_amount = $this->balanceCalculator->getPoolRemitmentAmount($pool, $session);
77
            });
78
    }
79
80
    /**
81
     * @param Session $session
82
     *
83
     * @return Collection
84
     */
85
    public function getAuctions(Session $session): Collection
86
    {
87
        return DB::table('auctions')
88
            ->select(DB::raw('sum(auctions.amount) as total'), 'subscriptions.pool_id')
89
            ->join('remitments', 'auctions.remitment_id', '=', 'remitments.id')
90
            ->join('payables', 'remitments.payable_id', '=', 'payables.id')
91
            ->join('subscriptions', 'payables.subscription_id', '=', 'subscriptions.id')
92
            ->where('auctions.session_id', $session->id)
93
            ->where('paid', true)
94
            ->groupBy('subscriptions.pool_id')
95
            ->pluck('total', 'pool_id');
96
    }
97
98
    /**
99
     * @param Closure $settlementFilter
100
     * @param Member $member|null
101
     *
102
     * @return Collection
103
     */
104
    private function getBills(Closure $settlementFilter, ?Member $member = null): Collection
105
    {
106
        $onetimeBillsQuery = DB::table('bills')
107
            ->join(DB::raw('onetime_bills as ob'), 'bills.id', '=', 'ob.bill_id')
108
            ->select(DB::raw('sum(bills.amount) as total_amount'),
109
                DB::raw('count(bills.id) as total_count'), 'ob.charge_id')
110
            ->groupBy('ob.charge_id')
111
            ->whereExists($settlementFilter)
112
            ->when($member !== null, fn($qm) =>
113
                $qm->where('ob.member_id', $member->id));
114
        $roundBillsQuery = DB::table('bills')
115
            ->join(DB::raw('round_bills as rb'), 'bills.id', '=', 'rb.bill_id')
116
            ->select(DB::raw('sum(bills.amount) as total_amount'),
117
                DB::raw('count(bills.id) as total_count'), 'rb.charge_id')
118
            ->groupBy('rb.charge_id')
119
            ->whereExists($settlementFilter)
120
            ->when($member !== null, fn($qm) =>
121
                $qm->where('rb.member_id', $member->id));
122
        $sessionBillsQuery = DB::table('bills')
123
            ->join(DB::raw('session_bills as sb'), 'bills.id', '=', 'sb.bill_id')
124
            ->select(DB::raw('sum(bills.amount) as total_amount'),
125
                DB::raw('count(bills.id) as total_count'), 'sb.charge_id')
126
            ->groupBy('sb.charge_id')
127
            ->whereExists($settlementFilter)
128
            ->when($member !== null, fn($qm) =>
129
                $qm->where('sb.member_id', $member->id));
130
        $libreBillsQuery = DB::table('bills')
131
            ->join(DB::raw('libre_bills as lb'), 'bills.id', '=', 'lb.bill_id')
132
            ->select(DB::raw('sum(bills.amount) as total_amount'),
133
                DB::raw('count(bills.id) as total_count'), 'lb.charge_id')
134
            ->groupBy('lb.charge_id')
135
            ->whereExists($settlementFilter)
136
            ->when($member !== null, fn($qm) =>
137
                $qm->where('lb.member_id', $member->id));
138
139
        return $onetimeBillsQuery
140
            ->union($roundBillsQuery)
141
            ->union($sessionBillsQuery)
142
            ->union($libreBillsQuery)
143
            ->get()
144
            ->keyBy('charge_id');
145
    }
146
147
    /**
148
     * @param Session $session
149
     * @param bool $onlyCurrent
150
     *
151
     * @return Collection
152
     */
153
    private function getDisbursedAmounts(Session $session, bool $onlyCurrent): Collection
154
    {
155
        return Outflow::select(DB::raw('sum(amount) as total_amount'),
156
                DB::raw('count(*) as total_count'), 'charge_id')
157
            ->groupBy('charge_id')
158
            ->whereHas('charge', fn($qc) => $qc->where('round_id', $session->round_id))
159
            ->when($onlyCurrent, fn($qo) => $qo->where('session_id', $session->id))
160
            ->when(!$onlyCurrent, fn($qo) =>
161
                $qo->whereHas('session', fn($qs) => $qs->precedes($session)))
162
            ->get()
163
            ->keyBy('charge_id');
164
    }
165
166
    /**
167
     * @param Session $session
168
     *
169
     * @return Collection
170
     */
171
    public function getSessionCharges(Session $session): Collection
172
    {
173
        $settlementFilter = fn(Builder $query) => $query
174
            ->select(DB::raw(1))
175
            ->from(DB::raw('settlements as st'))
0 ignored issues
show
Bug introduced by
Illuminate\Support\Facad...aw('settlements as st') of type Illuminate\Contracts\Database\Query\Expression is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $table of Illuminate\Database\Query\Builder::from(). ( Ignorable by Annotation )

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

175
            ->from(/** @scrutinizer ignore-type */ DB::raw('settlements as st'))
Loading history...
176
            ->where('session_id', $session->id)
177
            ->whereColumn('st.bill_id', 'bills.id');
178
        $bills = $this->getBills($settlementFilter);
179
        $outflows = $this->getDisbursedAmounts($session, true);
180
181
        return $session->round->charges
182
            ->each(function($charge) use($bills, $outflows) {
183
                $bill = $bills[$charge->id] ?? null;
184
                $charge->total_count = $bill ? $bill->total_count : 0;
185
                $charge->total_amount = $bill ? $bill->total_amount : 0;
186
                $charge->outflow = $outflows[$charge->id] ?? null;
187
            });
188
    }
189
190
    /**
191
     * @param Session $session
192
     * @param Member $member|null
193
     *
194
     * @return Collection
195
     */
196
    public function getTotalCharges(Session $session, ?Member $member = null): Collection
197
    {
198
        $sessionFilter = fn(Builder $query) => $query
199
            ->select(DB::raw(1))
200
            ->from(DB::raw('sessions as se'))
0 ignored issues
show
Bug introduced by
Illuminate\Support\Facad...::raw('sessions as se') of type Illuminate\Contracts\Database\Query\Expression is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $table of Illuminate\Database\Query\Builder::from(). ( Ignorable by Annotation )

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

200
            ->from(/** @scrutinizer ignore-type */ DB::raw('sessions as se'))
Loading history...
201
            ->whereColumn('se.id', 'st.session_id')
202
            ->where('se.round_id', $session->round_id)
203
            ->where('se.day_date', '<=', $session->day_date);
204
        $settlementFilter = fn(Builder $query) => $query
205
            ->select(DB::raw(1))
206
            ->from(DB::raw('settlements as st'))
0 ignored issues
show
Bug introduced by
Illuminate\Support\Facad...aw('settlements as st') of type Illuminate\Contracts\Database\Query\Expression is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $table of Illuminate\Database\Query\Builder::from(). ( Ignorable by Annotation )

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

206
            ->from(/** @scrutinizer ignore-type */ DB::raw('settlements as st'))
Loading history...
207
            ->whereExists($sessionFilter)
208
            ->whereColumn('st.bill_id', 'bills.id');
209
        $bills = $this->getBills($settlementFilter, $member);
210
        $outflows = $this->getDisbursedAmounts($session, false);
211
        if($member !== null)
212
        {
213
            // The outflow part of each member id calculated by dividing each amount
214
            // by the number of members.
215
            $memberCount = $session->round->members->count();
216
            foreach($outflows as $outflow)
217
            {
218
                $outflow->total_amount /= $memberCount;
219
            }
220
        }
221
222
        return $session->round->charges
223
            ->each(function($charge) use($bills, $outflows) {
224
                $bill = $bills[$charge->id] ?? null;
225
                $charge->total_count = $bill ? $bill->total_count : 0;
226
                $charge->total_amount = $bill ? $bill->total_amount : 0;
227
                $charge->outflow = $outflows[$charge->id] ?? null;
228
            });
229
    }
230
231
    /**
232
     * @param Session $session
233
     *
234
     * @return object
235
     */
236
    public function getLoan(Session $session): object
237
    {
238
        $loan = DB::table('loans')
239
            ->join('debts', 'loans.id', '=', 'debts.loan_id')
240
            ->select('debts.type', DB::raw('sum(debts.amount) as total_amount'))
241
            ->where('loans.session_id', $session->id)
242
            ->groupBy('debts.type')
243
            ->pluck('total_amount', 'type');
244
245
        return (object)[
246
            'principal' => $loan[Debt::TYPE_PRINCIPAL] ?? 0,
247
            'interest' => $loan[Debt::TYPE_INTEREST] ?? 0,
248
        ];
249
    }
250
251
    /**
252
     * @param Session $session
253
     *
254
     * @return object
255
     */
256
    public function getRefund(Session $session): object
257
    {
258
        $refund = $this->getRefundQuery()
259
            ->addSelect('debts.type')
260
            ->where('refunds.session_id', $session->id)
261
            ->groupBy('debts.type')
262
            ->pluck('total_amount', 'type');
263
        $partialRefund = DB::table('partial_refunds')
264
            ->join('debts', 'partial_refunds.debt_id', '=', 'debts.id')
265
            ->where('partial_refunds.session_id', $session->id)
266
            ->select('debts.type', DB::raw('sum(partial_refunds.amount) as total_amount'))
267
            ->groupBy('debts.type')
268
            ->pluck('total_amount', 'type');
269
270
        return (object)[
271
            'principal' => ($refund[Debt::TYPE_PRINCIPAL] ?? 0) +
272
                ($partialRefund[Debt::TYPE_PRINCIPAL] ?? 0),
273
            'interest' => ($refund[Debt::TYPE_INTEREST] ?? 0) +
274
                ($partialRefund[Debt::TYPE_INTEREST] ?? 0),
275
        ];
276
    }
277
278
    /**
279
     * @param Session $session
280
     *
281
     * @return object
282
     */
283
    public function getSaving(Session $session): object
284
    {
285
        $saving = DB::table('savings')
286
            ->select(DB::raw('sum(amount) as total_amount'),
287
                DB::raw('count(id) as total_count'))
288
            ->where('session_id', $session->id)
289
            ->first();
290
        if(!$saving->total_amount)
291
        {
292
            $saving->total_amount = 0;
293
        }
294
        if(!$saving->total_count)
295
        {
296
            $saving->total_count = 0;
297
        }
298
299
        return $saving;
300
    }
301
302
    /**
303
     * @param Session $session
304
     *
305
     * @return object
306
     */
307
    public function getOutflow(Session $session): object
308
    {
309
        $outflow = DB::table('outflows')
310
            ->select(DB::raw('sum(amount) as total_amount'),
311
                DB::raw('count(id) as total_count'))
312
            ->where('session_id', $session->id)
313
            ->first();
314
        if(!$outflow->total_amount)
315
        {
316
            $outflow->total_amount = 0;
317
        }
318
        if(!$outflow->total_count)
319
        {
320
            $outflow->total_count = 0;
321
        }
322
323
        return $outflow;
324
    }
325
}
326