whereIn("post_status", [ DonationStatus::COMPLETE, DonationStatus::RENEWAL ]) ->whereIn("ID", function (QueryBuilder $builder) use ($subscription) { $builder ->select("donation_id") ->from("give_donationmeta") ->where("meta_key", DonationMetaKeys::SUBSCRIPTION_ID) ->where("meta_value", $subscription->id); }) ->between( $yearStart->format("Y-m-d H:i:s"), $yearEnd->format("Y-m-d H:i:s") ) ->sumIntendedAmount(); $completedAmount = Money::fromDecimal($completedAmount, $subscription->amount->getCurrency()->getCode()); // Calculate projected amount based on remaining donations this year $remainingInstallments = $this->calculateRemainingInstallmentsUntilEndOfYear($subscription); $projectedAmount = $subscription->intendedAmount()->multiply($remainingInstallments); return $completedAmount->add($projectedAmount); } catch (Exception $e) { // Return zero Money object on error return new Money(0, $subscription->amount->getCurrency()->getCode()); } } /** * Calculate the remaining installments until the end of the current year. * * @since 4.8.0 */ private function calculateRemainingInstallmentsUntilEndOfYear(Subscription $subscription): int { $nextRenewal = clone $subscription->renewsAt; $currentYear = (new DateTime())->format('Y'); $yearEnd = new DateTime('last day of December ' . $currentYear . ' 23:59:59'); $modifier = $subscription->period->isQuarter() ? ($subscription->frequency * 3) . ' ' . SubscriptionPeriod::MONTH : $subscription->frequency . ' ' . $subscription->period->getValue(); $installments = 0; while ($nextRenewal <= $yearEnd) { if ($nextRenewal->format('Y') === $currentYear) { $installments++; } $nextRenewal->modify($modifier); } if ($subscription->installments > 0) { $paidInstallments = $subscription->donations()->count(); $remainingInstallments = max(0, $subscription->installments - $paidInstallments); $installments = min($installments, $remainingInstallments); } return $installments; } }