PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.3.21
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.3.21
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 1.3.21, at app/Services/Report/Concerns/Subscription/FutureRenewals.php

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