PluginProbe
GiveWP – Donation Plugin and Fundraising Platform / 4.16.8.1
GiveWP – Donation Plugin and Fundraising Platform v4.16.8.1
4.16.8.1 4.16.8 4.16.7.2 4.16.7.1 4.16.7 4.16.6.1 4.16.6 4.16.5.1 4.16.5 4.16.4 4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 All 254 releases
give / src / Subscriptions / Models / Subscription.php

Subscription.php in GiveWP – Donation Plugin and Fundraising Platform 4.16.8.1, at src/Subscriptions/Models/Subscription.php

388 lines 9.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Give\Subscriptions\Models;
4
5 use DateTime;
6 use Exception;
7 use Give\Campaigns\Models\Campaign;
8 use Give\Donations\Models\Donation;
9 use Give\Donors\Models\Donor;
10 use Give\Framework\Models\Contracts\ModelCrud;
11 use Give\Framework\Models\Contracts\ModelHasFactory;
12 use Give\Framework\Models\Model;
13 use Give\Framework\Models\ModelQueryBuilder;
14 use Give\Framework\Models\ValueObjects\Relationship;
15 use Give\Framework\PaymentGateways\PaymentGateway;
16 use Give\Framework\Support\ValueObjects\Money;
17 use Give\Subscriptions\Actions\GenerateNextRenewalForSubscription;
18 use Give\Subscriptions\Actions\CalculateProjectedAnnualRevenue;
19 use Give\Subscriptions\DataTransferObjects\SubscriptionQueryData;
20 use Give\Subscriptions\Factories\SubscriptionFactory;
21 use Give\Subscriptions\ValueObjects\SubscriptionMode;
22 use Give\Subscriptions\ValueObjects\SubscriptionPeriod;
23 use Give\Subscriptions\ValueObjects\SubscriptionStatus;
24
25
26 /**
27 * Class Subscription
28 *
29 * @since 4.11.0 added campaign ID property
30 * @since 4.10.0 added campaign relationship
31 * @since 2.23.0 added the renewsAt property
32 * @since 2.19.6
33 *
34 * @property int $id
35 * @property int $donationFormId
36 * @property int $campaignId
37 * @property DateTime $createdAt
38 * @property DateTime $renewsAt The date the subscription will renew next
39 * @property int $donorId
40 * @property SubscriptionPeriod $period
41 * @property int $frequency
42 * @property int $installments The total number of installments for the subscription; discontinues after this number of installments
43 * @property string $transactionId
44 * @property SubscriptionMode $mode
45 * @property Money $amount
46 * @property Money $feeAmountRecovered
47 * @property SubscriptionStatus $status
48 * @property string $gatewayId
49 * @property string $gatewaySubscriptionId
50 * @property Donor $donor
51 * @property Donation[] $donations
52 * @property float $projectedAnnualRevenue
53 * @property ?Campaign $campaign
54 */
55 class Subscription extends Model implements ModelCrud, ModelHasFactory
56 {
57 /**
58 * @inheritdoc
59 */
60 protected $properties = [
61 'id' => 'int',
62 'donationFormId' => 'int',
63 'campaignId' => 'int',
64 'createdAt' => DateTime::class,
65 'renewsAt' => DateTime::class,
66 'donorId' => 'int',
67 'period' => SubscriptionPeriod::class,
68 'frequency' => 'int',
69 'installments' => ['int', 0],
70 'transactionId' => 'string',
71 'mode' => SubscriptionMode::class,
72 'amount' => Money::class,
73 'feeAmountRecovered' => Money::class,
74 'status' => SubscriptionStatus::class,
75 'gatewaySubscriptionId' => ['string', ''],
76 'gatewayId' => 'string',
77 'projectedAnnualRevenue' => Money::class,
78 ];
79
80 /**
81 * @inheritdoc
82 */
83 protected $relationships = [
84 'donor' => Relationship::BELONGS_TO,
85 'donations' => Relationship::HAS_MANY,
86 'notes' => Relationship::HAS_MANY,
87 'campaign' => Relationship::BELONGS_TO,
88 ];
89
90 /**
91 * Find subscription by ID
92 *
93 * @since 2.19.6
94 *
95 * @param int $id
96 *
97 * @return Subscription|null
98 */
99 public static function find($id)
100 {
101 return give()->subscriptions->getById($id);
102 }
103
104 /**
105 * @since 2.19.6
106 *
107 * @return ModelQueryBuilder<Donor>
108 */
109 public function donor(): ModelQueryBuilder
110 {
111 return give()->donors->queryById($this->donorId);
112 }
113
114 /**
115 * @since 2.19.6
116 *
117 * @return ModelQueryBuilder<Donation>
118 */
119 public function donations(): ModelQueryBuilder
120 {
121 return give()->donations->queryBySubscriptionId($this->id);
122 }
123
124 /**
125 * @since 4.8.0
126 *
127 * @return ModelQueryBuilder<SubscriptionNote>
128 */
129 public function notes(): ModelQueryBuilder
130 {
131 return give()->subscriptions->notes->queryBySubscriptionId($this->id);
132 }
133
134 /**
135 * Get Subscription notes
136 *
137 * @deprecated Access notes via $subscription->notes()->getAll() instead.
138 * @since 2.19.6
139 *
140 * @return object[]
141 */
142 public function getNotes(): array
143 {
144 _give_deprecated_function(__METHOD__, '4.6.0', '$subscription->notes()->getAll()');
145
146 return give()->subscriptions->getNotesBySubscriptionId($this->id);
147 }
148
149 /**
150 * Returns the subscription amount the donor "intended", which means it is the amount without recovered fees. So if the
151 * donor paid $100, but the donation was charged $105 with a $5 fee, this method will return $100.
152 *
153 * @since 2.20.0
154 */
155 public function intendedAmount(): Money
156 {
157 return $this->feeAmountRecovered === null
158 ? $this->amount
159 : $this->amount->subtract($this->feeAmountRecovered);
160 }
161
162 /**
163 * Bumps the subscription's renewsAt date to the next renewal date.
164 *
165 * @return void
166 */
167 public function bumpRenewalDate()
168 {
169 $this->renewsAt = give(GenerateNextRenewalForSubscription::class)(
170 $this->period,
171 $this->frequency,
172 $this->renewsAt
173 );
174 }
175
176 /**
177 * Returns the donation that began the subscription.
178 *
179 * @since 4.8.0 Returns null if no initial donation found.
180 * @since 2.23.0
181 */
182 public function initialDonation(): ?Donation
183 {
184 $initialDonationId = give()->subscriptions->getInitialDonationId($this->id);
185
186 if (!$initialDonationId) {
187 return null;
188 }
189
190 return Donation::find(give()->subscriptions->getInitialDonationId($this->id));
191 }
192
193 /**
194 * @since 2.20.0 return mutated model instance
195 * @since 2.19.6
196 *
197 * @throws Exception
198 */
199 public static function create(array $attributes): Subscription
200 {
201 $subscription = new static($attributes);
202 $subscription->save();
203
204 return $subscription;
205 }
206
207 /**
208 * @since 3.20.0
209 * @throws Exception
210 */
211 public function createRenewal(array $attributes = []): Donation
212 {
213 return give()->subscriptions->createRenewal($this, $attributes);
214 }
215
216 /**
217 * @since 3.20.0
218 */
219 public function shouldCreateRenewal(): bool
220 {
221 if (!$this->status->isActive()) {
222 return false;
223 }
224
225 return $this->isIndefinite() || $this->totalDonations() < $this->installments;
226 }
227
228 /**
229 * @since 3.20.0
230 */
231 public function totalDonations(): int
232 {
233 return $this->donations()->count();
234 }
235
236 /**
237 * @since 3.20.0
238 */
239 public function shouldEndSubscription(): bool
240 {
241 if ($this->isIndefinite()) {
242 return false;
243 }
244
245 return $this->totalDonations() >= $this->installments;
246 }
247
248 /**
249 * @since 2.20.0 mutate model in repository and return void
250 * @since 2.19.6
251 *
252 * @return void
253 * @throws Exception
254 */
255 public function save()
256 {
257 if (!$this->id) {
258 give()->subscriptions->insert($this);
259 } else {
260 give()->subscriptions->update($this);
261 }
262 }
263
264 /**
265 * @since 2.19.6
266 *
267 * @throws Exception
268 */
269 public function delete(): bool
270 {
271 return give()->subscriptions->delete($this);
272 }
273
274 /**
275 * @since 4.8.0
276 */
277 public function trash(): bool
278 {
279 return give()->subscriptions->trash($this);
280 }
281
282 /**
283 * @since 4.12.0
284 */
285 public function unTrash(): bool
286 {
287 return give()->subscriptions->unTrash($this);
288 }
289
290 /**
291 * @since 2.20.0
292 *
293 * @param bool $force Set to true to ignore the status of the subscription
294 *
295 * @throws Exception
296 */
297 public function cancel(bool $force = false)
298 {
299 if (!$force && $this->status->isCancelled()) {
300 return;
301 }
302
303 $this->gateway()->cancelSubscription($this);
304 }
305
306 /**
307 * @since 2.24.0
308 */
309 public function isIndefinite(): bool
310 {
311 return $this->installments === 0;
312 }
313
314 /**
315 * @since 2.24.0
316 *
317 * @return int|float
318 */
319 public function remainingInstallments()
320 {
321 return $this->isIndefinite() ? INF : ($this->installments - $this->donations()->count());
322 }
323
324 /**
325 * @since 2.24.0
326 *
327 * @return boolean
328 */
329 public function hasExceededTheMaxInstallments(): bool
330 {
331 return 0 > $this->remainingInstallments();
332 }
333
334 /**
335 * @since 2.19.6
336 *
337 * @return ModelQueryBuilder<Subscription>
338 */
339 public static function query(): ModelQueryBuilder
340 {
341 return give()->subscriptions->prepareQuery();
342 }
343
344 /**
345 * @since 2.19.6
346 *
347 * @param object $object
348 */
349 public static function fromQueryBuilderObject($object): Subscription
350 {
351 return SubscriptionQueryData::fromObject($object)->toSubscription();
352 }
353
354 /**
355 * @return PaymentGateway
356 */
357 public function gateway(): PaymentGateway
358 {
359 return give()->gateways->getPaymentGateway($this->gatewayId);
360 }
361
362 /**
363 * @since 2.19.6
364 */
365 public static function factory(): SubscriptionFactory
366 {
367 return new SubscriptionFactory(static::class);
368 }
369
370 /**
371 * @since 4.8.0
372 */
373 public function projectedAnnualRevenue(): Money
374 {
375 return give(CalculateProjectedAnnualRevenue::class)($this);
376 }
377
378 /**
379 * @since 4.10.0
380 *
381 * @return ModelQueryBuilder<Campaign>
382 */
383 public function campaign(): ModelQueryBuilder
384 {
385 return give()->campaigns->queryByFormId($this->donationFormId);
386 }
387 }
388