PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / trunk
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler vtrunk
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
fluent-cart / app / Services / Report / Concerns / Subscription / FutureRenewals.php

FutureRenewals.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler trunk, at app/Services/Report/Concerns/Subscription/FutureRenewals.php

216 lines 6.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\Report\Concerns\Subscription;
4
5 use FluentCart\App\App;
6 use FluentCart\App\Services\DateTime\DateTime;
7 use FluentCart\App\Services\Payments\PaymentHelper;
8
9 trait FutureRenewals
10 {
11 public function getFutureRenewals(array $params)
12 {
13 $startDate = (new DateTime)->startOfDay();
14 $endDate = (new DateTime)->addQuarter()->endOfDay();
15 // $groupBy = $params['groupBy'] ?? 'daily'; // daily or monthly
16 $groupBy = 'monthly';
17
18 $activeSubscriptions = $this->getActiveSubscriptions();
19 $projections = [];
20 $totalProjected = 0;
21 $totalRenewals = 0;
22
23 // Initialize grouped data structure
24 $current = clone $startDate;
25 while ($current <= $endDate) {
26 $key = $groupBy === 'daily'
27 ? $current->format('Y-m-d')
28 : $current->format('Y-m');
29
30 if (!isset($projections[$key])) {
31 $projections[$key] = [
32 'group' => $key,
33 'renewals_count' => 0,
34 'projected_amount' => 0,
35 ];
36 }
37
38 if ($groupBy === 'daily') {
39 $current->addDay();
40 } else {
41 $current->addMonth();
42 }
43 }
44
45 foreach ($activeSubscriptions as $subscription) {
46 $renewals = $this->calculateRenewalsInPeriod(
47 $subscription,
48 $startDate,
49 $endDate
50 );
51
52 $projectedAmount = $renewals * $subscription->recurring_total;
53 $totalProjected += $projectedAmount;
54
55 // Get renewal dates for this subscription to group them
56 $renewalDates = $this->getRenewalDatesInPeriod($subscription, $startDate, $endDate);
57
58 foreach ($renewalDates as $renewalDate) {
59 $groupKey = $groupBy === 'daily'
60 ? $renewalDate->format('Y-m-d')
61 : $renewalDate->format('Y-m');
62
63 if (isset($projections[$groupKey])) {
64 $projections[$groupKey]['renewals_count']++;
65 $totalRenewals++;
66 $projections[$groupKey]['projected_amount'] += $subscription->recurring_total;
67 }
68 }
69 }
70
71 return [
72 'totalProjected' => $totalProjected,
73 'totalRenewals' => $totalRenewals,
74 'projections' => array_values($projections),
75 'period' => [
76 $startDate->format('Y-m-d H:i:s'),
77 $endDate->format('Y-m-d H:i:s'),
78 ],
79 'groupBy' => $groupBy,
80 ];
81 }
82
83 /**
84 * Get all active subscriptions with billing info
85 */
86 private function getActiveSubscriptions()
87 {
88 return App::db()->table('fct_subscriptions')
89 ->select([
90 'id',
91 'recurring_total',
92 'billing_interval',
93 'next_billing_date',
94 'status',
95 'expire_at',
96 'bill_times',
97 'bill_count',
98 ])
99 ->whereIn('status', ['active', 'trialing']) // Active statuses
100 ->whereNotNull('next_billing_date')
101 ->where(function ($query) {
102 $query->whereNull('expire_at')
103 ->orWhere('expire_at', '>', gmdate('Y-m-d H:i:s'));
104 })
105 ->get();
106 }
107
108 /**
109 * Calculate how many renewals will occur for a subscription in the given period
110 */
111 private function calculateRenewalsInPeriod($subscription, $startDate, $endDate)
112 {
113 if (!$subscription->next_billing_date) {
114 return 0;
115 }
116
117 $nextBilling = new DateTime($subscription->next_billing_date);
118 $periodStart = new DateTime($startDate);
119 $periodEnd = new DateTime($endDate);
120
121 // If next billing is after the end period, no renewals
122 if ($nextBilling > $periodEnd) {
123 return 0;
124 }
125
126 // Check if subscription has limited billing cycles
127 if ($subscription->bill_times > 0) {
128 $remainingBills = $subscription->bill_times - $subscription->bill_count;
129 if ($remainingBills <= 0) {
130 return 0;
131 }
132 }
133
134 $renewalCount = 0;
135 $currentBilling = clone $nextBilling;
136 $intervalDays = $this->getIntervalDays($subscription->billing_interval);
137
138 if ($intervalDays < 1) { // unresolved interval would never advance the loop
139 return 0;
140 }
141
142 while ($currentBilling <= $periodEnd) {
143 if ($currentBilling >= $periodStart) {
144 $renewalCount++;
145
146 // Check if we've reached the billing limit
147 if ($subscription->bill_times > 0 &&
148 ($subscription->bill_count + $renewalCount) >= $subscription->bill_times) {
149 break;
150 }
151 }
152 $currentBilling->modify("+{$intervalDays} days");
153 }
154
155 return $renewalCount;
156 }
157
158 /**
159 * Convert billing interval to days
160 */
161 private function getIntervalDays($interval)
162 {
163 return PaymentHelper::getIntervalDays($interval);
164 }
165
166 /**
167 * Get actual renewal dates for a subscription within the period
168 */
169 private function getRenewalDatesInPeriod($subscription, $startDate, $endDate)
170 {
171 $renewalDates = [];
172
173 if (!$subscription->next_billing_date) {
174 return $renewalDates;
175 }
176
177 $nextBilling = new DateTime($subscription->next_billing_date);
178 $periodStart = new DateTime($startDate);
179 $periodEnd = new DateTime($endDate);
180
181 if ($nextBilling > $periodEnd) {
182 return $renewalDates;
183 }
184
185 if ($subscription->bill_times > 0) {
186 $remainingBills = $subscription->bill_times - $subscription->bill_count;
187 if ($remainingBills <= 0) {
188 return $renewalDates;
189 }
190 }
191
192 $currentBilling = clone $nextBilling;
193 $intervalDays = $this->getIntervalDays($subscription->billing_interval);
194 $renewalCount = 0;
195
196 if ($intervalDays < 1) { // unresolved interval would never advance the loop
197 return $renewalDates;
198 }
199
200 while ($currentBilling <= $periodEnd) {
201 if ($currentBilling >= $periodStart) {
202 $renewalDates[] = clone $currentBilling;
203 $renewalCount++;
204
205 if ($subscription->bill_times > 0 &&
206 ($subscription->bill_count + $renewalCount) >= $subscription->bill_times) {
207 break;
208 }
209 }
210 $currentBilling->modify("+{$intervalDays} days");
211 }
212
213 return $renewalDates;
214 }
215 }
216