Passed
Push — main ( ef0742...4e298b )
by Thierry
11:40
created

SessionService::getBills()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 41
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 38
c 2
b 0
f 0
nc 1
nop 2
dl 0
loc 41
rs 9.312

1 Method

Rating   Name   Duplication   Size   Complexity  
A SessionService::getDisbursedAmounts() 0 11 1
1
<?php
2
3
namespace Siak\Tontine\Service\Report;
4
5
use Closure;
6
use Illuminate\Database\Eloquent\Builder as ElBuilder;
7
use Illuminate\Database\Query\Builder;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\Facades\DB;
10
use Siak\Tontine\Model\Debt;
11
use Siak\Tontine\Model\Deposit;
12
use Siak\Tontine\Model\Member;
13
use Siak\Tontine\Model\Outflow;
14
use Siak\Tontine\Model\Session;
15
use Siak\Tontine\Service\Payment\BalanceCalculator;
16
17
class SessionService
18
{
19
    use Traits\Queries;
20
21
    /**
22
     * @param BalanceCalculator $balanceCalculator
23
     */
24
    public function __construct(private BalanceCalculator $balanceCalculator)
25
    {}
26
27
    /**
28
     * @param Session $session
29
     *
30
     * @return Collection
31
     */
32
    public function getReceivables(Session $session): Collection
33
    {
34
        return $session->pools()
35
            ->addSelect([
36
                'paid_amount' => Deposit::select(DB::raw('sum(amount)'))
37
                    ->whereColumn('pool_id', 'pools.id')
38
                    ->whereHas('receivable', fn(ElBuilder $qr) =>
39
                        $qr->whereSession($session)),
40
            ])
41
            ->withCount([
42
                'receivables as total_count' => fn(ElBuilder $query) =>
43
                    $query->whereSession($session),
44
                'receivables as paid_count' => fn(ElBuilder $query) =>
45
                    $query->whereSession($session)->paidHere($session),
46
                'receivables as late_count' => fn(ElBuilder $query) =>
47
                    $query->whereSession($session)->paidLater($session),
48
            ])
49
            ->get()
50
            ->each(function($pool) {
51
                $pool->paid_amount ??= 0;
52
                $pool->total_amount = $pool->amount * $pool->total_count;
53
            });
54
    }
55
56
    /**
57
     * @param Session $session
58
     *
59
     * @return Collection
60
     */
61
    public function getPayables(Session $session): Collection
62
    {
63
        return $session->pools()
64
            ->withCount([
65
                'sessions',
66
                'payables as total_count' => fn(ElBuilder $query) =>
67
                    $query->whereSession($session),
68
                'payables as paid_count' => fn(ElBuilder $query) =>
69
                    $query->whereSession($session)->whereHas('remitment'),
70
            ])
71
            ->get()
72
            ->each(function($pool) use($session) {
73
                $pool->total_amount = $pool->amount * $pool->total_count * $pool->sessions_count;
74
                $pool->paid_amount = $this->balanceCalculator->getPoolRemitmentAmount($pool, $session);
75
            });
76
    }
77
78
    /**
79
     * @param Session $session
80
     *
81
     * @return Collection
82
     */
83
    public function getAuctions(Session $session): Collection
84
    {
85
        return DB::table('auctions')
86
            ->select(DB::raw('sum(auctions.amount) as total'), 'subscriptions.pool_id')
87
            ->join('remitments', 'auctions.remitment_id', '=', 'remitments.id')
88
            ->join('payables', 'remitments.payable_id', '=', 'payables.id')
89
            ->join('subscriptions', 'payables.subscription_id', '=', 'subscriptions.id')
90
            ->where('auctions.session_id', $session->id)
91
            ->where('paid', true)
92
            ->groupBy('subscriptions.pool_id')
93
            ->pluck('total', 'pool_id');
94
    }
95
96
    /**
97
     * @param Closure $settlementFilter
98
     * @param Member $member|null
99
     *
100
     * @return Collection
101
     */
102
    private function getSettlements(Closure $settlementFilter, ?Member $member = null): Collection
103
    {
104
        return DB::table('v_settlements')
105
            ->select(DB::raw('sum(amount) as total_amount'),
106
                DB::raw('count(*) as total_count'), 'charge_id')
107
            ->groupBy('charge_id')
108
            ->where($settlementFilter)
109
            ->when($member !== null, fn($qm) =>
110
                $qm->where('member_id', $member->id))
111
            ->get()
112
            ->keyBy('charge_id');
113
    }
114
115
    /**
116
     * @param Session $session
117
     * @param bool $onlyCurrent
118
     *
119
     * @return Collection
120
     */
121
    private function getDisbursedAmounts(Session $session, bool $onlyCurrent): Collection
122
    {
123
        return Outflow::select(DB::raw('sum(amount) as total_amount'),
124
                DB::raw('count(*) as total_count'), 'charge_id')
125
            ->groupBy('charge_id')
126
            ->whereHas('charge', fn($qc) => $qc->where('round_id', $session->round_id))
127
            ->when($onlyCurrent, fn($qo) => $qo->where('session_id', $session->id))
128
            ->when(!$onlyCurrent, fn($qo) =>
129
                $qo->whereHas('session', fn($qs) => $qs->precedes($session)))
130
            ->get()
131
            ->keyBy('charge_id');
132
    }
133
134
    /**
135
     * @param Session $session
136
     *
137
     * @return Collection
138
     */
139
    public function getSessionCharges(Session $session): Collection
140
    {
141
        $settlementFilter = fn(Builder $qs) =>
142
            $qs->where('session_id', $session->id);
143
        $settlements = $this->getSettlements($settlementFilter);
144
        $outflows = $this->getDisbursedAmounts($session, true);
145
146
        return $session->round->charges
147
            ->map(function($charge) use($settlements, $outflows) {
148
                $settlement = $settlements[$charge->id] ?? null;
149
                // We need to clone the model, or else the getTotalCharges()
150
                // method will modify and return the same object and values.
151
                $charge = $charge->replicate();
152
                $charge->total_count = $settlement?->total_count ?? 0;
153
                $charge->total_amount = $settlement?->total_amount ?? 0;
154
                $charge->outflow = $outflows[$charge->id] ?? null;
155
                return $charge;
156
            });
157
    }
158
159
    /**
160
     * @param Session $session
161
     * @param Member $member|null
162
     *
163
     * @return Collection
164
     */
165
    public function getTotalCharges(Session $session, ?Member $member = null): Collection
166
    {
167
        $settlementFilter = fn(Builder $qs) =>
168
            $qs->whereIn('session_id', DB::table('sessions')
169
                ->select('id')
170
                ->where('round_id', $session->round_id)
171
                ->where('day_date', '<=', $session->day_date));
172
        $settlements = $this->getSettlements($settlementFilter, $member);
173
        $outflows = $this->getDisbursedAmounts($session, false);
174
        if($member !== null)
175
        {
176
            // The outflow part of each member id calculated by dividing each amount
177
            // by the number of members.
178
            $memberCount = $session->round->members->count();
179
            foreach($outflows as $outflow)
180
            {
181
                $outflow->total_amount /= $memberCount;
182
            }
183
        }
184
185
        return $session->round->charges
186
            ->map(function($charge) use($settlements, $outflows) {
187
                $settlement = $settlements[$charge->id] ?? null;
188
                // We need to clone the model, or else the getSessionCharges()
189
                // method will modify and return the same object and values.
190
                $charge = $charge->replicate();
191
                $charge->total_count = $settlement?->total_count ?? 0;
192
                $charge->total_amount = $settlement?->total_amount ?? 0;
193
                $charge->outflow = $outflows[$charge->id] ?? null;
194
                return $charge;
195
            });
196
    }
197
198
    /**
199
     * @param Session $session
200
     *
201
     * @return object
202
     */
203
    public function getLoan(Session $session): object
204
    {
205
        $loan = DB::table('loans')
206
            ->join('debts', 'loans.id', '=', 'debts.loan_id')
207
            ->select('debts.type', DB::raw('sum(debts.amount) as total_amount'))
208
            ->where('loans.session_id', $session->id)
209
            ->groupBy('debts.type')
210
            ->pluck('total_amount', 'type');
211
212
        return (object)[
213
            'principal' => $loan[Debt::TYPE_PRINCIPAL] ?? 0,
214
            'interest' => $loan[Debt::TYPE_INTEREST] ?? 0,
215
        ];
216
    }
217
218
    /**
219
     * @param Session $session
220
     *
221
     * @return object
222
     */
223
    public function getRefund(Session $session): object
224
    {
225
        $refund = $this->getRefundQuery()
226
            ->addSelect('debts.type')
227
            ->where('refunds.session_id', $session->id)
228
            ->groupBy('debts.type')
229
            ->pluck('total_amount', 'type');
230
        $partialRefund = DB::table('partial_refunds')
231
            ->join('debts', 'partial_refunds.debt_id', '=', 'debts.id')
232
            ->where('partial_refunds.session_id', $session->id)
233
            ->select('debts.type', DB::raw('sum(partial_refunds.amount) as total_amount'))
234
            ->groupBy('debts.type')
235
            ->pluck('total_amount', 'type');
236
237
        return (object)[
238
            'principal' => ($refund[Debt::TYPE_PRINCIPAL] ?? 0) +
239
                ($partialRefund[Debt::TYPE_PRINCIPAL] ?? 0),
240
            'interest' => ($refund[Debt::TYPE_INTEREST] ?? 0) +
241
                ($partialRefund[Debt::TYPE_INTEREST] ?? 0),
242
        ];
243
    }
244
245
    /**
246
     * @param Session $session
247
     *
248
     * @return object
249
     */
250
    public function getSaving(Session $session): object
251
    {
252
        $saving = DB::table('savings')
253
            ->select(DB::raw('sum(amount) as total_amount'),
254
                DB::raw('count(id) as total_count'))
255
            ->where('session_id', $session->id)
256
            ->first();
257
        if(!$saving->total_amount)
258
        {
259
            $saving->total_amount = 0;
260
        }
261
        if(!$saving->total_count)
262
        {
263
            $saving->total_count = 0;
264
        }
265
266
        return $saving;
267
    }
268
269
    /**
270
     * @param Session $session
271
     *
272
     * @return object
273
     */
274
    public function getOutflow(Session $session): object
275
    {
276
        $outflow = DB::table('outflows')
277
            ->select(DB::raw('sum(amount) as total_amount'),
278
                DB::raw('count(id) as total_count'))
279
            ->where('session_id', $session->id)
280
            ->first();
281
        if(!$outflow->total_amount)
282
        {
283
            $outflow->total_amount = 0;
284
        }
285
        if(!$outflow->total_count)
286
        {
287
            $outflow->total_count = 0;
288
        }
289
290
        return $outflow;
291
    }
292
}
293