Version 4.6.0

This commit is contained in:
Neo committed 2025-12-19 05:55:31 -08:00
1 parent f79dcf067a
commit 517a5e1f70
2036 files changed
+110041 -26162

No files matched your search

@@ -114,19 +114,17 @@ class _checkout extends \IPS\Dispatcher\Controller
{
$showFirstStep = TRUE;
}
if ( !$showFirstStep )
foreach ( \IPS\nexus\Customer\CustomField::roots() as $field )
{
foreach ( \IPS\nexus\Customer\CustomField::roots() as $field )
$column = $field->column;
if ( $field->purchase_show and $field->purchase_require and !$this->invoice->member->$column )
{
$column = $field->column;
if ( $field->purchase_show and $field->purchase_require and !$this->invoice->member->$column )
{
$showFirstStep = TRUE;
$canSkipFirstStepIfNameAndBillingAddressIsKnown = FALSE;
break;
}
$showFirstStep = TRUE;
$canSkipFirstStepIfNameAndBillingAddressIsKnown = FALSE;
break;
}
}
}
}
else
{
@@ -182,6 +180,7 @@ class _checkout extends \IPS\Dispatcher\Controller
/* Init */
$buttonLang = 'continue_to_review';
$needBillingInfo = ( $this->invoice->hasItemsRequiringBillingAddress() or $this->invoice->hasPhysicalItems() );
$needTaxStatus = NULL;
if ( $this->invoice->hasPhysicalItems() )
{
@@ -254,6 +253,26 @@ class _checkout extends \IPS\Dispatcher\Controller
}
if ( $needBillingInfo )
{
/* Do we need to know if they are a business or consumer? */
foreach ( $this->invoice->items as $item )
{
if ( $tax = $item->tax )
{
if ( $tax->type === 'eu' )
{
$needTaxStatus = 'eu';
break;
}
if ( $tax->type === 'business' )
{
$needTaxStatus = 'business';
}
}
}
$addressHelperClass = $needTaxStatus ? 'IPS\nexus\Form\BusinessAddress' : 'IPS\Helpers\Form\Address';
$addressHelperOptions = ( $needTaxStatus === 'eu' ) ? array( 'vat' => TRUE ) : array();
/* The actual billing address */
$addresses = \IPS\Db::i()->select( '*', 'nexus_customer_addresses', array( '`member`=?', \IPS\Member::loggedIn()->member_id ) );
if ( \count( $addresses ) )
{
@@ -261,7 +280,7 @@ class _checkout extends \IPS\Dispatcher\Controller
$options = array();
foreach ( new \IPS\Patterns\ActiveRecordIterator( $addresses, 'IPS\nexus\Customer\Address' ) as $address )
{
$options[ $address->id ] = $address->address->toString('<br>');
$options[ $address->id ] = $address->address->toString('<br>') . ( ( isset( $address->address->business ) and $address->address->business and isset( $address->address->vat ) and $address->address->vat ) ? ( '<br>' . \IPS\Member::loggedIn()->language()->addToStack('cm_checkout_vat_number') . ': ' . mb_strtoupper( preg_replace( '/[^A-Z0-9]/', '', $address->address->vat ) ) ) : '' );
if ( ( !$this->invoice->billaddress and $address->primary_billing ) or $this->invoice->billaddress == $address->address )
{
$billing = $address->id;
@@ -270,13 +289,13 @@ class _checkout extends \IPS\Dispatcher\Controller
$options[0] = \IPS\Member::loggedIn()->language()->addToStack( 'other' );
$form->add( new \IPS\Helpers\Form\Radio( 'billing_address', $billing, TRUE, array( 'options' => $options, 'toggles' => array( 0 => array( 'new_billing_address' ) ), 'parse' => 'raw' ) ) );
$newAddress = new \IPS\Helpers\Form\Address( 'new_billing_address', !$billing ? $this->invoice->billaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_billing_address' );
$newAddress = new $addressHelperClass( 'new_billing_address', !$billing ? $this->invoice->billaddress : NULL, FALSE, $addressHelperOptions, NULL, NULL, NULL, 'new_billing_address' );
$newAddress->label = ' ';
$form->add( $newAddress );
}
else
{
$form->add( new \IPS\Helpers\Form\Address( 'new_billing_address', $this->invoice->billaddress, TRUE ) );
$form->add( new $addressHelperClass( 'new_billing_address', $this->invoice->billaddress, TRUE, $addressHelperOptions ) );
}
}
@@ -521,10 +540,24 @@ class _checkout extends \IPS\Dispatcher\Controller
$member->cm_first_name = $values['cm_first_name'];
$member->cm_last_name = $values['cm_last_name'];
if( !\IPS\Login::usernameIsInUse( "{$values['cm_first_name']} {$values['cm_last_name']}" ) )
/* If this name is available, use that, otherwise append a number to the name to avoid an incomplete account. */
$i = 0;
do
{
$member->name = "{$values['cm_first_name']} {$values['cm_last_name']}";
$name = "{$values['cm_first_name']} {$values['cm_last_name']}";
if ( $i > 0 )
{
$name .= $i;
}
if ( !\IPS\Login::usernameIsInUse( $name ) )
{
$member->name = $name;
break;
}
$i++;
}
while( TRUE );
}
$member->email = $values['email_address'];
$member->setLocalPassword( $values['password'] );
@@ -935,7 +968,7 @@ class _checkout extends \IPS\Dispatcher\Controller
/* Remove any existing shipping charges on the invoice */
foreach ( $this->invoice->items as $k => $v )
{
if ( $v instanceof \IPS\nexus\extensions\nexus\Item\ ShippingCharge )
if ( $v instanceof \IPS\nexus\extensions\nexus\Item\ShippingCharge )
{
$this->invoice->removeItem( $k );
}
@@ -1134,6 +1167,21 @@ class _checkout extends \IPS\Dispatcher\Controller
{
\IPS\Output::i()->error( 'err_no_methods', '5X196/8', 500, '' );
}
/* Verify stock level one last time. It's possible someone added an item to their cart, then someone else did and checked out and the stock level is now 0. */
foreach ( $this->invoice->items as $item )
{
if( $item->appKey == 'nexus' AND $item->typeKey == 'package' )
{
$package = \IPS\nexus\Package::load( $item->id );
$data = $package->optionValuesStockAndPrice( $package->optionValues( $item->details ) );
if ( $data['stock'] != -1 and $data['stock'] < $item->quantity )
{
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'not_enough_in_stock_checkout', FALSE, array( 'pluralize' => array( $data['stock'] ), 'sprintf' => array( $item->name ) ) ), '1X196/G', 403, '' );
}
}
}
/* Work out recurring payments */
$recurrings = array();
@@ -1223,50 +1271,54 @@ class _checkout extends \IPS\Dispatcher\Controller
}
}
/* If there's something to pay, we'll need to look at the gateways... */
/* Get available payment methods, removing any that aren't supported by the items on this invoice */
$paymentMethods = array();
foreach ( \IPS\nexus\Gateway::roots() as $gateway )
{
$paymentMethods[ $gateway->id ] = $gateway;
}
$canUseAccountCredit = TRUE;
foreach ( $this->invoice->items as $item )
{
if ( $item->paymentMethodIds )
{
foreach ( $paymentMethods as $k => $v )
{
if ( \in_array( '*', $item->paymentMethodIds ) ) // This looks odd but in older versions IPS\nexus\extensions\nexus\Item\Subscription::renewalPaymentMethodIds() was mistakenly returning array('*') and since the value is stored in the invoice we have to keep this for compatibility with invoices created at that time
{
continue;
}
if ( !\in_array( $k, $item->paymentMethodIds ) )
{
unset( $paymentMethods[ $k ] );
}
}
}
if ( !$item::$canUseAccountCredit )
{
$canUseAccountCredit = FALSE;
}
}
/* If there's something to pay, show a form for it... */
$paymentType = NULL;
$hiddenValues = array();
if ( $amountToPay->amount->isGreaterThanZero() )
{
$paymentType = 'pay';
/* Get available payment methods */
$paymentMethods = array();
foreach ( \IPS\nexus\Gateway::roots() as $gateway )
/* Remove any payment methods that can't be used for this transaction */
foreach ( $paymentMethods as $gateway )
{
if ( $gateway->checkValidity( $amountToPay, $this->invoice->billaddress, \IPS\nexus\Customer::loggedIn()->member_id ? \IPS\nexus\Customer::loggedIn() : \IPS\nexus\Customer::constructFromData( $this->invoice->guest_data['member'] ), $recurrings ) )
if ( !$gateway->checkValidity( $amountToPay, $this->invoice->billaddress, \IPS\nexus\Customer::loggedIn()->member_id ? \IPS\nexus\Customer::loggedIn() : \IPS\nexus\Customer::constructFromData( $this->invoice->guest_data['member'] ), $recurrings ) )
{
$paymentMethods[ $gateway->id ] = $gateway;
unset( $paymentMethods[ $gateway->id ] );
}
}
/* Remove any not supported by items */
$canUseAccountCredit = TRUE;
foreach ( $this->invoice->items as $item )
{
if ( $item->paymentMethodIds )
{
foreach ( $paymentMethods as $k => $v )
{
if ( \in_array( '*', $item->paymentMethodIds ) ) // This looks odd but in older versions IPS\nexus\extensions\nexus\Item\Subscription::renewalPaymentMethodIds() was mistakenly returning array('*') and since the value is stored in the invoice we have to keep this for compatibility with invoices created at that time
{
continue;
}
if ( !\in_array( $k, $item->paymentMethodIds ) )
{
unset( $paymentMethods[ $k ] );
}
}
}
if ( !$item::$canUseAccountCredit )
{
$canUseAccountCredit = FALSE;
}
}
/* If we don't have any, show an error */
/* If we don't have any available payment methods, show an error */
if ( \count( $paymentMethods ) === 0 and !$amountToPay->amount->isZero() )
{
\IPS\Output::i()->error( 'err_no_methods', '4X196/3', 500, 'err_no_methods_admin' );
@@ -1312,7 +1364,7 @@ class _checkout extends \IPS\Dispatcher\Controller
}
}
/* If there's nothing to pay now, but there will be something to pay later... if we DON'T have any stored payment methods, we may be able to prompt the user to create one */
elseif ( $recurrings and \IPS\nexus\Gateway::cardStorageGateways() and !\IPS\Db::i()->select( 'COUNT(*)', 'nexus_customer_cards', array( 'card_member=?', \IPS\nexus\Customer::loggedIn()->member_id ) )->first() and $elements = \IPS\nexus\Customer\CreditCard::createFormElements( \IPS\nexus\Customer::loggedIn(), FALSE, $showSubmitButton, $hiddenValues ) )
elseif ( $recurrings and array_intersect( array_keys( $paymentMethods ), array_keys( \IPS\nexus\Gateway::cardStorageGateways() ) ) and !\IPS\Db::i()->select( 'COUNT(*)', 'nexus_customer_cards', array( 'card_member=?', \IPS\nexus\Customer::loggedIn()->member_id ) )->first() and $elements = \IPS\nexus\Customer\CreditCard::createFormElements( \IPS\nexus\Customer::loggedIn(), FALSE, $showSubmitButton, $hiddenValues ) )
{
$paymentType = 'card';
}
@@ -1444,8 +1496,8 @@ class _checkout extends \IPS\Dispatcher\Controller
$transaction->save();
$credits[ $this->invoice->currency ]->amount = $credits[ $this->invoice->currency ]->amount->subtract( $transaction->amount->amount );
$this->invoice->member->cm_credits = $credits;
$this->invoice->member->save();
\IPS\nexus\Customer::loggedIn()->cm_credits = $credits;
\IPS\nexus\Customer::loggedIn()->save();
$this->invoice->member->log( 'transaction', array(
'type' => 'paid',
@@ -1565,9 +1617,6 @@ class _checkout extends \IPS\Dispatcher\Controller
/* Check if there's anything still to pay */
if ( $this->invoice->amountToPay()->amount->isZero() )
{
/* Mark the invoice paid */
$this->invoice->markPaid();
/* Redirect */
$destination = $this->invoice->return_uri ?: $this->invoice->url();
if ( \IPS\Member::loggedIn()->member_id )