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')) |
|
|
|
|
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')) |
|
|
|
|
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')) |
|
|
|
|
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
|
|
|
|