Version 4.7.23
This commit is contained in:
1 parent
7124a02564
commit
25ddeb65d6
1791 files changed
+76990
-44452
No files matched your search
@@ -12,6 +12,9 @@
|
||||
namespace IPS\nexus\Subscription;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
|
||||
use IPS\DateTime;
|
||||
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
@@ -653,6 +656,13 @@ class _Package extends \IPS\Node\Model
|
||||
$priceOfThisPackage = $priceOfThisPackage[ $currency ]['amount'];
|
||||
$renewalOptionsOnNewPackage = json_decode( $this->renew_options, TRUE );
|
||||
|
||||
/* If the price of the package is greater than 0, and the renewals are 0, then this is a lifetime purchase,
|
||||
so ignore any renewals */
|
||||
if( $priceOfThisPackage > 0 and !empty( $renewalOptionsOnNewPackage ) and $renewalOptionsOnNewPackage['cost'][ $currency ]['amount'] == 0 )
|
||||
{
|
||||
$renewalOptionsOnNewPackage = null;
|
||||
}
|
||||
|
||||
/* It's a non-recurring subscription */
|
||||
if ( empty( $renewalOptionsOnNewPackage ) )
|
||||
{
|
||||
@@ -683,13 +693,13 @@ class _Package extends \IPS\Node\Model
|
||||
{
|
||||
case -1:
|
||||
return NULL; /* nope */
|
||||
|
||||
|
||||
case 0:
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
|
||||
|
||||
case 1:
|
||||
/* If the purchase is expired, charge the full amount */
|
||||
if( !$purchase->active )
|
||||
if( !$purchase->active or ( $purchase->expire instanceof \IPS\DateTime and $purchase->expire->getTimestamp() < time() ) )
|
||||
{
|
||||
$newPrice = new \IPS\nexus\Money( $priceOfThisPackage, $currency );
|
||||
|
||||
@@ -703,11 +713,12 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
|
||||
return new \IPS\nexus\Money( $priceOfThisPackage - $priceOfExistingPackage, $currency );
|
||||
|
||||
|
||||
case 2:
|
||||
if ( !$purchase->renewals )
|
||||
/* If there are no renewals, we charge full price no matter what */
|
||||
if ( !$purchase->renewals or !$purchase->expire )
|
||||
{
|
||||
return new \IPS\nexus\Money( 0, $currency );
|
||||
return new \IPS\nexus\Money( $priceOfThisPackage, $currency );
|
||||
}
|
||||
if ( !$renewalOptionsOnNewPackage )
|
||||
{
|
||||
@@ -716,7 +727,7 @@ class _Package extends \IPS\Node\Model
|
||||
|
||||
/* What is the closest renewal option on the new package? We'll use that one */
|
||||
$renewalOptionsInDays = array();
|
||||
$term = ( new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $renewalOptionsOnNewPackage['cost'][ $currency ]['amount'], $currency ), new \DateInterval( 'P' . $renewalOptionsOnNewPackage['term'] . mb_strtoupper( $renewalOptionsOnNewPackage['unit'] ) ), $purchase->renewals->tax ) );
|
||||
$term = ( new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $renewalOptionsOnNewPackage['cost'][ $currency ]['amount'], $currency ), new \DateInterval( 'P' . $renewalOptionsOnNewPackage['term'] . mb_strtoupper( $renewalOptionsOnNewPackage['unit'] ) ), $purchase->renewals?->tax ) );
|
||||
$renewalOptionsInDays[ $term->days() ] = $term;
|
||||
|
||||
$closestRenewalOption = null;
|
||||
@@ -729,42 +740,87 @@ class _Package extends \IPS\Node\Model
|
||||
}
|
||||
}
|
||||
$renewalTermToUse = $renewalOptionsInDays[ $closestRenewalOption ];
|
||||
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( (string) round( $numberOfDaysInCurrentRenewalTerm ) );
|
||||
|
||||
/* If the purchase is not active, it is the full cost */
|
||||
if( !$purchase->active )
|
||||
if( !$purchase->active or ( $purchase->expire instanceof \IPS\DateTime and $purchase->expire->getTimestamp() < time() ) )
|
||||
{
|
||||
return new \IPS\nexus\Money( $renewalTermToUse->cost->amount, $currency );
|
||||
}
|
||||
|
||||
/* Current Period Start Date */
|
||||
$startOfCurrentPeriod = $purchase->expire->sub( $purchase->renewals->interval );
|
||||
|
||||
/* DateInterval of time between subscription start and now */
|
||||
$timeSinceStartOfCurrentPeriod = $startOfCurrentPeriod->diff( \IPS\DateTime::create() );
|
||||
$numberOfDaysInCurrentRenewalTerm = new \IPS\Math\Number( (string) round( $numberOfDaysInCurrentRenewalTerm ) );
|
||||
|
||||
/* Count Days in Period */
|
||||
$daysUsedInPeriod = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSinceStartOfCurrentPeriod ) );
|
||||
$daysRemainingInPeriod = new \IPS\Math\Number( (string) $numberOfDaysInCurrentRenewalTerm->subtract( $daysUsedInPeriod ) );
|
||||
|
||||
/* Currency Worth of value used in current sub */
|
||||
$valueUsedOfSubscription = $purchase->renewals->cost->amount->divide( $numberOfDaysInCurrentRenewalTerm )->multiply( $daysUsedInPeriod );
|
||||
|
||||
if ( !$daysRemainingInPeriod->isGreaterThanZero() )
|
||||
if( $purchase->expire instanceof \IPS\DateTime and $purchase->renewals )
|
||||
{
|
||||
return new \IPS\nexus\Money( new \IPS\Math\Number('0'), $currency );
|
||||
}
|
||||
elseif( $priceOfExistingPackage > $priceOfThisPackage )
|
||||
{
|
||||
/* If this is a downgrade, calculate a refund */
|
||||
$refund = $purchase->renewals->cost->amount->subtract( $valueUsedOfSubscription );
|
||||
return new \IPS\nexus\Money( $refund->multiply( new \IPS\Math\Number( "-1" ) ), $currency );
|
||||
$startOfCurrentPeriod = $purchase->expire->sub( $purchase->renewals->interval );
|
||||
|
||||
/* If we renewed for multiple cycles, we might be in the future, so go back */
|
||||
$cycles = 1;
|
||||
while( $startOfCurrentPeriod->diff( \IPS\DateTime::create() )->invert )
|
||||
{
|
||||
$startOfCurrentPeriod = $startOfCurrentPeriod->sub( $purchase->renewals->interval );
|
||||
$cycles++;
|
||||
}
|
||||
|
||||
if( $cycles > 1 )
|
||||
{
|
||||
$numberOfDaysInCurrentRenewalTerm = $numberOfDaysInCurrentRenewalTerm->multiply( new \IPS\Math\Number( "{$cycles}" ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new \IPS\nexus\Money( $renewalTermToUse->cost->amount->subtract( $valueUsedOfSubscription ), $currency );
|
||||
/* If the purchase does not expire, we have to do some fun logic here */
|
||||
$timeSincePurchase = $purchase->start->diff( \IPS\DateTime::create() );
|
||||
$daysUsedSincePurchase = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSincePurchase ) );
|
||||
|
||||
/* If we used less than a full renewal period, then set the start to the purchase date */
|
||||
if( $daysUsedSincePurchase->compare( $numberOfDaysInCurrentRenewalTerm ) <= 0 )
|
||||
{
|
||||
$startOfCurrentPeriod = $purchase->start;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise loop through all the used intervals */
|
||||
$start = clone $purchase->start;
|
||||
while( $daysUsedSincePurchase->compare( $numberOfDaysInCurrentRenewalTerm ) > 0 )
|
||||
{
|
||||
$start = $start->add( $purchase->renewals->interval );
|
||||
$timeSincePurchase = $start->diff( \IPS\DateTime::create() );
|
||||
$daysUsedSincePurchase = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSincePurchase ) );
|
||||
}
|
||||
$startOfCurrentPeriod = $start;
|
||||
}
|
||||
}
|
||||
|
||||
/* DateInterval of time between subscription start and now */
|
||||
$timeSinceStartOfCurrentPeriod = $startOfCurrentPeriod->diff( \IPS\DateTime::create() );
|
||||
|
||||
/* Count Days in Period */
|
||||
$daysUsedInPeriod = new \IPS\Math\Number( (string)\IPS\DateTime::intervalToDays( $timeSinceStartOfCurrentPeriod ) );
|
||||
|
||||
/* Count how many days we have left to our current subscription so we can calculate how much we are owed */
|
||||
$daysRemainingInPeriod = $numberOfDaysInCurrentRenewalTerm->subtract( $daysUsedInPeriod );
|
||||
$valueRemainingInSubscription = $purchase->renewals ? $purchase->renewals->costPerDay()->amount->multiply( $daysRemainingInPeriod ) : new \IPS\Math\Number( '0' );
|
||||
|
||||
/* Start with 0 and subtract what we are still owed */
|
||||
$cost = new \IPS\Math\Number( "0" );
|
||||
$cost = $cost->subtract( $valueRemainingInSubscription );
|
||||
|
||||
/* Now add the cost of the new package */
|
||||
$valueRemainingInThisPackage = $renewalTermToUse->costPerDay()->amount->multiply( $daysRemainingInPeriod );
|
||||
$cost = $cost->add( $valueRemainingInThisPackage );
|
||||
|
||||
/* But what if our current expiration is multiple cycles ahead? */
|
||||
if( $purchase->expire instanceof \IPS\DateTime and $purchase->renewals->days() < $renewalTermToUse->days())
|
||||
{
|
||||
$calculatedExpiration = $purchase->expire->sub( $renewalTermToUse->interval );
|
||||
while( $calculatedExpiration->diff( \IPS\DateTime::create() )->invert )
|
||||
{
|
||||
$cost = $cost->add( new \IPS\Math\Number( $priceOfThisPackage ) );
|
||||
$calculatedExpiration = $calculatedExpiration->sub( $renewalTermToUse->interval );
|
||||
}
|
||||
}
|
||||
|
||||
return new \IPS\nexus\Money( $cost, $currency );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,7 +943,7 @@ class _Package extends \IPS\Node\Model
|
||||
$purchase->expire = NULL;
|
||||
}
|
||||
/* We have a term, but the unit is different from the existing package */
|
||||
elseif ( $purchase->expire and $term and $oldTerm and ( $rawTerm['unit'] != $oldTerm['unit'] ) )
|
||||
elseif ( $purchase->expire and $term and $oldTerm and ( $rawTerm['unit'] != $oldTerm['unit'] or $rawTerm['term'] != $oldTerm['term'] ) )
|
||||
{
|
||||
/* If there is an upgrade cost, the expire date should be from when the current billing period started. */
|
||||
if( $costToUpgrade->amount->isGreaterThanZero() AND \IPS\Settings::i()->nexus_subs_upgrade > 0 )
|
||||
@@ -927,7 +983,7 @@ class _Package extends \IPS\Node\Model
|
||||
$purchase->save();
|
||||
|
||||
/* Re-add usergroups */
|
||||
$newPackage->_addUsergroups( $purchase->member );
|
||||
$newPackage->_addUsergroups( $purchase->member, TRUE );
|
||||
|
||||
/* Cancel any pending invoices */
|
||||
if ( $pendingInvoice = $purchase->invoice_pending )
|
||||
@@ -1132,13 +1188,20 @@ class _Package extends \IPS\Node\Model
|
||||
* Add user groups
|
||||
*
|
||||
* @param \IPS\nexus\Customer $member The customer
|
||||
* @param bool $isPackageChange true if this is an upgrade/downgrade
|
||||
* @return void
|
||||
*/
|
||||
public function _addUsergroups( $member )
|
||||
public function _addUsergroups( $member, $isPackageChange=false )
|
||||
{
|
||||
$previousGroup = 0;
|
||||
$previousSecondary = '';
|
||||
$newSecondary = '';
|
||||
$current = null;
|
||||
try
|
||||
{
|
||||
$current = \IPS\Db::i()->select( 'sub_previous_group,sub_previous_secondary_groups', 'nexus_member_subscriptions', array( 'sub_active=? and sub_member_id=? and sub_package_id=?', 1, $member->member_id, $this->id ) )->first();
|
||||
}
|
||||
catch( \UnderflowException ){}
|
||||
|
||||
/* Primary Group */
|
||||
if ( $this->primary_group and $this->primary_group != $member->member_group_id and !\in_array( $member->member_group_id, explode( ',', \IPS\Settings::i()->nexus_subs_exclude_groups ) ) )
|
||||
@@ -1199,7 +1262,21 @@ class _Package extends \IPS\Node\Model
|
||||
$member->logHistory( 'core', 'group', array( 'type' => 'secondary', 'by' => 'subscription', 'action' => 'add', 'id' => $this->id, 'old' => $previousSecondary, 'new' => $newSecondary ) );
|
||||
}
|
||||
|
||||
\IPS\Db::i()->update( 'nexus_member_subscriptions', array( 'sub_previous_group' => $previousGroup, 'sub_previous_secondary_groups' => $previousSecondary ), array( 'sub_active=1 and sub_package_id=? and sub_member_id=?', $this->id, $member->member_id ) );
|
||||
/* We only want to update the group demotions if this is a new subscription,
|
||||
OR if the previous subscription did not have a demotion */
|
||||
$update = [];
|
||||
if( !$isPackageChange OR $current === null OR !$current['sub_previous_group'] )
|
||||
{
|
||||
$update[ 'sub_previous_group' ] = $previousGroup;
|
||||
}
|
||||
if( !$isPackageChange OR $current === null OR !$current['sub_previous_secondary_groups'] )
|
||||
{
|
||||
$update['sub_previous_secondary_groups'] = $previousSecondary;
|
||||
}
|
||||
if( \count( $update ) )
|
||||
{
|
||||
\IPS\Db::i()->update( 'nexus_member_subscriptions', $update, array( 'sub_active=1 and sub_member_id=?', $member->member_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user