Version 5.0.0 beta 1

This commit is contained in:
Neo committed 2025-12-19 16:27:35 -08:00
1 parent 25ddeb65d6
commit 15c7beabc5
6736 files changed
+627902 -497943

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -12,24 +12,33 @@
namespace IPS\nexus\Invoice\Item;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\nexus\Invoice\Item;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invoice Item Class for Charges
*/
abstract class _Charge extends \IPS\nexus\Invoice\Item
abstract class Charge extends Item
{
/**
* @brief Act (new/charge)
*/
public static $act = 'charge';
public static string $act = 'charge';
/**
* @brief Requires login to purchase?
*/
public static $requiresAccount = FALSE;
public static bool $requiresAccount = FALSE;
/**
* @brief Can use coupons?
*/
public static bool $canUseCoupons = false;
}
+193 -131
View File
@@ -12,140 +12,148 @@
namespace IPS\nexus\Invoice;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use BadMethodCallException;
use DomainException;
use IPS\File;
use IPS\GeoLocation;
use IPS\Http\Url;
use IPS\Math\Number;
use IPS\Member;
use IPS\nexus\Coupon;
use IPS\nexus\Customer;
use IPS\nexus\Invoice;
use IPS\nexus\Money;
use IPS\nexus\Purchase;
use IPS\nexus\Tax;
use IPS\Settings;
use IPS\Theme;
use function defined;
use function gettype;
use function in_array;
use function is_int;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invoice Abstract Item Interface
*/
abstract class _Item
abstract class Item
{
/**
* @brief Can use coupons?
*/
public static $canUseCoupons = TRUE;
public static bool $canUseCoupons = TRUE;
/**
* @brief Can use account credit?
*/
public static $canUseAccountCredit = TRUE;
public static bool $canUseAccountCredit = TRUE;
/**
* @brief Can you buy more than one of this item?
*
* @var bool
*/
public static bool $canChangeQuantity = false;
/**
* @brief Icon
*/
public static string $icon = '';
/**
* @brief string Name
*/
public $name;
public string $name;
/**
* @brief int Quantity
*/
public $quantity = 1;
public int $quantity = 1;
/**
* @brief \IPS\nexus\Money Price
*/
public $price;
public Money $price;
/**
* @brief int|NULL ID
*/
public $id;
public ?int $id;
/**
* @brief \IPS\nexus\Tax Tax Class
*/
public $tax;
public ?Tax $tax = null;
/**
* @brief Payment Methods IDs
*/
public $paymentMethodIds;
public ?array $paymentMethodIds = null;
/**
* @brief Key/Value array of extra details to display on the invoice
*/
public $details = array();
public array $details = array();
/**
* @brief Key/Value array of extra details to store on the purchase
*/
public $purchaseDetails = array();
public array $purchaseDetails = array();
/**
* @brief Physical?
*/
public $physical = FALSE;
/**
* @brief Shipping Methods IDs
*/
public $shippingMethodIds;
/**
* @brief Chosen Shipping Methods ID
*/
public $chosenShippingMethodId;
/**
* @brief Weight
*/
public $weight;
/**
* @brief Length
*/
public $length;
/**
* @brief Width
*/
public $width;
/**
* @brief Height
*/
public $height;
/**
* @brief Pay To member
*/
public $payTo;
public ?Member $payTo = null;
/**
* @brief Application
*/
public static $application;
public static string $application = '';
/**
* @brief Commission percentage
*/
public $commission = 0;
public int $commission = 0;
/**
* @brief Commission fee
*/
public $fee = 0;
public int|Money $fee = 0;
/**
* @brief Extra
*/
public $extra;
public mixed $extra = null;
/**
* @brief Group With Parent
*/
public $groupWithParent = FALSE;
public bool $groupWithParent = FALSE;
/**
* @brief Application key
*/
public ?string $appKey = NULL;
/**
* @brief Type key
*/
public ?string $typeKey = NULL;
/**
* Constructor
*
* @param string $name Name
* @param \IPS\nexus\Money $price Price
* @param string $name Name
* @param Money $price Price
* @return void
*/
public function __construct( $name, \IPS\nexus\Money $price )
public function __construct(string $name, Money $price )
{
$this->name = $name;
$this->price = $price;
@@ -157,53 +165,117 @@ abstract class _Item
* @param string $k Property
* @return mixed
*/
public function __get( $k )
public function __get( string $k ) : mixed
{
$k = mb_substr( $k, 1 );
return static::$$k;
}
/**
* Used to override the static property, if necessary
*
* @return bool
*/
public function canUseAccountCredit() : bool
{
return static::$canUseAccountCredit;
}
/**
* Used to override the static property, if necessary
*
* @return bool
*/
public function canUseCoupons() : bool
{
return static::$canUseCoupons;
}
/**
* @return bool
*/
public function canChangeQuantity() : bool
{
return static::$canChangeQuantity;
}
/**
* Determines if the coupon can be applied to this item
*
* @param array|string $data
* @param Invoice $invoice
* @param Customer $customer
* @return bool
*/
public function isValid( array|string $data, Invoice $invoice, Customer $customer ) : bool
{
return false;
}
/**
* Get line price without tax
*
* @return \IPS\nexus\Money
* @return Money
*/
public function linePrice()
public function linePrice() : Money
{
return new \IPS\nexus\Money( $this->price->amount->multiply( new \IPS\Math\Number("{$this->quantity}") ), $this->price->currency );
return new Money( $this->price->amount->multiply( new Number("{$this->quantity}") ), $this->price->currency );
}
/**
* Get tax rate
*
* @param \IPS\GeoLocation|NULL $location Location to use for tax rate or NULL to use specified billing address
* @return \IPS\Math\Number
* @param GeoLocation|NULL $location Location to use for tax rate or NULL to use specified billing address
* @return Number
*/
public function taxRate( \IPS\GeoLocation $location = NULL )
public function taxRate( ?GeoLocation $location = NULL ) : Number
{
return new \IPS\Math\Number( $this->tax ? $this->tax->rate( $location ) : '0' );
return new Number( $this->tax ? $this->tax->rate( $location ) : '0' );
}
/**
* Get item price with tax
*
* @param \IPS\GeoLocation|NULL $location Location to use for tax rate or NULL to use specified billing address
* @return \IPS\nexus\Money
* @param GeoLocation|NULL $location Location to use for tax rate or NULL to use specified billing address
* @return Money
*/
public function grossPrice( \IPS\GeoLocation $location = NULL )
public function grossPrice( ?GeoLocation $location = NULL ) : Money
{
return new \IPS\nexus\Money( $this->price->amount->add( $this->price->amount->multiply( $this->taxRate( $location ) ) ), $this->price->currency );
return new Money( $this->price->amount->add( $this->price->amount->multiply( $this->taxRate( $location ) ) ), $this->price->currency );
}
/**
* Get line price with tax
*
* @param \IPS\GeoLocation|NULL $location Location to use for tax rate or NULL to use specified billing address
* @return \IPS\nexus\Money
* @param GeoLocation|NULL $location Location to use for tax rate or NULL to use specified billing address
* @return Money
*/
public function grossLinePrice( \IPS\GeoLocation $location = NULL )
public function grossLinePrice( ?GeoLocation $location = NULL ) : Money
{
return new \IPS\nexus\Money( $this->linePrice()->amount->add( $this->linePrice()->amount->multiply( $this->taxRate( $location ) ) ), $this->price->currency );
return new Money( $this->linePrice()->amount->add( $this->linePrice()->amount->multiply( $this->taxRate( $location ) ) ), $this->price->currency );
}
/**
* Format the details to be displayed on the invoice
* Moved to a method so that custom items can add line items
* that may not be custom fields
*
* @param string $location checkout|invoice|print
* @return string
*/
public function detailsForDisplay( string $location ) : string
{
switch( $location )
{
case 'checkout':
return Theme::i()->getTemplate( 'checkout', 'nexus', 'front' )->packageFields( $this );
case 'print':
return Theme::i()->getTemplate( 'invoices', 'nexus', 'global' )->packageFields( $this );
default:
return Theme::i()->getTemplate( 'clients', 'nexus', 'front' )->packageFields( $this );
}
}
/**
@@ -211,7 +283,7 @@ abstract class _Item
*
* @return array
*/
public function recipientAmounts()
public function recipientAmounts() : array
{
$return = array();
@@ -221,19 +293,19 @@ abstract class _Item
$currency = $linePrice->currency;
$commission = $this->price->amount->percentage( $this->commission );
$lineComission = $commission->multiply( new \IPS\Math\Number("{$this->quantity}") );
$lineComission = $commission->multiply( new Number("{$this->quantity}") );
$return['site_commission_unit'] = new \IPS\nexus\Money( $commission, $currency );
$return['site_commission_line'] = new \IPS\nexus\Money( $lineComission, $currency );
$return['site_commission_unit'] = new Money( $commission, $currency );
$return['site_commission_line'] = new Money( $lineComission, $currency );
$return['recipient_unit'] = new \IPS\nexus\Money( $this->price->amount->subtract( $return['site_commission_unit']->amount ), $currency );
$return['recipient_line'] = new \IPS\nexus\Money( $linePrice->amount->subtract( $return['site_commission_line']->amount ), $currency );
$return['recipient_unit'] = new Money( $this->price->amount->subtract( $return['site_commission_unit']->amount ), $currency );
$return['recipient_line'] = new Money( $linePrice->amount->subtract( $return['site_commission_line']->amount ), $currency );
$fee = $this->fee ? $this->fee->amount : new \IPS\Math\Number('0');
$fee = $this->fee ? $this->fee->amount : new Number('0');
$siteTotal = $return['site_commission_line']->amount->add( $fee );
$recipientTotal = $linePrice->amount->subtract( $siteTotal );
$return['site_total'] = new \IPS\nexus\Money( $siteTotal, $currency );
$return['recipient_final'] = new \IPS\nexus\Money( $recipientTotal->isGreaterThanZero() ? $recipientTotal : 0, $currency );
$return['site_total'] = new Money( $siteTotal, $currency );
$return['recipient_final'] = new Money( $recipientTotal->isGreaterThanZero() ? $recipientTotal : 0, $currency );
}
else
{
@@ -246,14 +318,14 @@ abstract class _Item
/**
* Get amount for recipient (on line price)
*
* @return \IPS\nexus\Money
* @throws \BadMethodCallException
* @return Money
* @throws BadMethodCallException
*/
public function amountForRecipient()
public function amountForRecipient() : Money
{
if ( !$this->payTo )
{
throw new \BadMethodCallException;
throw new BadMethodCallException;
}
$recipientAmount = $this->recipientAmounts();
@@ -263,9 +335,9 @@ abstract class _Item
/**
* Image
*
* @return |IPS\File|NULL
* @return File|null
*/
public function image()
public function image(): File|null
{
return NULL;
}
@@ -273,10 +345,10 @@ abstract class _Item
/**
* On Paid
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @return void
* @param Invoice $invoice The invoice
* @return void
*/
public function onPaid( \IPS\nexus\Invoice $invoice )
public function onPaid( Invoice $invoice ): void
{
}
@@ -284,10 +356,10 @@ abstract class _Item
/**
* On Unpaid description
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @param Invoice $invoice The invoice
* @return array
*/
public function onUnpaidDescription( \IPS\nexus\Invoice $invoice )
public function onUnpaidDescription( Invoice $invoice ): array
{
return array();
}
@@ -295,11 +367,11 @@ abstract class _Item
/**
* On Unpaid
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @param Invoice $invoice The invoice
* @param string $status Status
* @return void
* @return void
*/
public function onUnpaid( \IPS\nexus\Invoice $invoice, $status )
public function onUnpaid( Invoice $invoice, string $status ): void
{
}
@@ -307,10 +379,10 @@ abstract class _Item
/**
* On Invoice Cancel (when unpaid)
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @return void
* @param Invoice $invoice The invoice
* @return void
*/
public function onInvoiceCancel( \IPS\nexus\Invoice $invoice )
public function onInvoiceCancel( Invoice $invoice ): void
{
}
@@ -321,11 +393,11 @@ abstract class _Item
* is ran to check the items they are purchasing can be bought.
* Is expected to throw a DomainException with an error message to display to the user if not valid
*
* @param \IPS\Member $member The new member
* @return void
* @throws \DomainException
* @param Member $member The new member
* @return void
* @throws DomainException
*/
public function memberCanPurchase( \IPS\Member $member )
public function memberCanPurchase( Member $member ): void
{
}
@@ -334,19 +406,19 @@ abstract class _Item
* Requires Billing Address
*
* @return bool
* @throws \DomainException
* @throws DomainException
*/
public function requiresBillingAddress()
public function requiresBillingAddress(): bool
{
return \in_array( 'other', explode( ',', \IPS\Settings::i()->nexus_require_billing ) );
return in_array( 'other', explode( ',', Settings::i()->nexus_require_billing ) );
}
/**
* Client Area URL
*
* @return |IPS\Http\Url|NULL
* @return Url|string|NULL
*/
public function url()
function url(): Url|string|null
{
return NULL;
}
@@ -354,9 +426,9 @@ abstract class _Item
/**
* ACP URL
*
* @return |IPS\Http\Url|NULL
* @return Url|null
*/
public function acpUrl()
public function acpUrl(): Url|null
{
return NULL;
}
@@ -365,16 +437,16 @@ abstract class _Item
* Is this item the same as another item in the cart?
* Used to decide when an item is added to the cart if we should just increase the quantity of this item instead of creating a new item.
*
* @param \IPS\nexus\Invoice\Item $item The other item
* @return bool
* @param Item $item The other item
* @return bool
*/
public function isSameAsOtherItem( $item )
public function isSameAsOtherItem( Item $item ): bool
{
/* If one is set to be associated with something in the cart and the other is set to be associated
with an existing purchase, trying to compare those will throw an error */
if ( isset( $item->parent ) and isset( $this->parent ) )
{
if ( \gettype( $this->parent ) != \gettype( $item->parent ) )
if ( gettype( $this->parent ) != gettype( $item->parent ) )
{
return FALSE;
}
@@ -391,7 +463,7 @@ abstract class _Item
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @param Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse string name Item name
* @apiresponse string itemApp Key for application. For example, 'nexus' for products and renewals; 'downloads' for Downloads files
@@ -403,11 +475,6 @@ abstract class _Item
* @apiresponse \IPS\nexus\Money itemPrice Item price, before tax
* @apiresponse \IPS\nexus\Money linePrice Line price, before tax
* @apiresponse \IPS\nexus\Tax taxClassId If the item should be taxed, the Tax Class that applies
* @apiresponse bool physical If the item is physical
* @apiresponse float weight If the item is physical, the weight in kilograms
* @apiresponse float length If the item is physical, the length in metres
* @apiresponse float width If the item is physical, the width in metres
* @apiresponse float height If the item is physical, the height in metres
* @apiresponse \IPS\nexus\Purchase\RenewalTerm renewalTerm If the item renews, the renewal term
* @apiresponse datetime expireDate If the item has been set to expire at a certain date but not automatically renew, the dare it will expire
* @apiresponse object details The values for any custom package fields
@@ -418,7 +485,7 @@ abstract class _Item
* @apiresponse float commission If the payment for this item goes to another user (for example for Downloads files), the percentage of the price that will be retained by the site (in addition to fee)
* @apiresponse \IPS\nexus\Money fee If the payment for this item goes to another user (for example for Downloads files), the fee that will be deducted by the site (in addition to commission)
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
public function apiOutput( Member $authorizedMember = NULL ): array
{
return array(
'name' => $this->name,
@@ -430,19 +497,14 @@ abstract class _Item
'quantity' => $this->quantity,
'itemPrice' => $this->price->apiOutput( $authorizedMember ),
'linePrice' => $this->linePrice()->apiOutput( $authorizedMember ),
'taxClass' => $this->tax ? $this->tax->apiOutput( $authorizedMember ) : null,
'physical' => $this->physical,
'weight' => isset( $this->weight ) ? $this->weight->kilograms : null,
'length' => isset( $this->length ) ? $this->length->metres : null,
'width' => isset( $this->width ) ? $this->width->metres : null,
'height' => isset( $this->height ) ? $this->height->metres : null,
'taxClass' => $this->tax?->apiOutput($authorizedMember),
'renewalTerm' => isset( $this->renewalTerm ) ? $this->renewalTerm->apiOutput( $authorizedMember ) : null,
'expireDate' => isset( $this->expireDate ) ? $this->expireDate->rfc3339() : null,
'details' => $this->details,
'parentPurchase' => ( isset( $this->parent ) and $this->parent instanceof \IPS\nexus\Purchase ) ? $this->parent->apiOutput( $authorizedMember ) : null,
'parentItem' => ( isset( $this->parent ) and \is_int( $this->parent ) ) ? $this->parent : null,
'groupWithParent' => isset( $this->groupWithParent ) ? $this->groupWithParent : false,
'payTo' => $this->payTo ? $this->payTo->apiOutput( $authorizedMember ) : null,
'parentPurchase' => ( isset( $this->parent ) and $this->parent instanceof Purchase ) ? $this->parent->apiOutput( $authorizedMember ) : null,
'parentItem' => ( isset( $this->parent ) and is_int( $this->parent ) ) ? $this->parent : null,
'groupWithParent' => $this->groupWithParent ?? false,
'payTo' => $this->payTo?->apiOutput($authorizedMember),
'commission' => $this->commission,
'fee' => $this->fee ? $this->fee->apiOutput( $authorizedMember ) : null,
);
@@ -12,59 +12,87 @@
namespace IPS\nexus\Invoice\Item;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use DomainException;
use IPS\DateTime;
use IPS\Db;
use IPS\File;
use IPS\Helpers\Form;
use IPS\Helpers\Form\Date;
use IPS\Helpers\Form\Interval;
use IPS\Helpers\Form\Node;
use IPS\Helpers\Form\Text;
use IPS\Http\Url;
use IPS\Member;
use IPS\nexus\Coupon;
use IPS\nexus\Customer;
use IPS\nexus\Form\RenewalTerm;
use IPS\nexus\Invoice;
use IPS\nexus\Invoice\Item;
use IPS\nexus\Purchase as NexusPurchase;
use IPS\nexus\Purchase\RenewalTerm as PurchaseRenewalTerm;
use IPS\nexus\Tax;
use IPS\Patterns\ActiveRecordIterator;
use IPS\Settings;
use OutOfRangeException;
use function defined;
use function in_array;
use function is_array;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invoice Item Class for Purchases
*/
abstract class _Purchase extends \IPS\nexus\Invoice\Item
abstract class Purchase extends Item
{
/**
* @brief string Act (new/charge)
*/
public static $act = 'new';
public static string $act = 'new';
/**
* @brief Requires login to purchase?
*/
public static $requiresAccount = TRUE;
public static bool $requiresAccount = TRUE;
/**
* @brief \DateInterval Length granted by initial purchase before normal renewal term starts (or NULL to match renewal term)
*/
public $initialInterval;
public ?DateInterval $initialInterval = null;
/**
* @brief \IPS\nexus\Purchase\RenewalTerm Renewal Term
*/
public $renewalTerm;
public ?PurchaseRenewalTerm $renewalTerm = null;
/**
* @brief \IPS\DateTime Expiry Date (only if the purchase needs to expire but not renew)
*/
public $expireDate;
public ?DateTime $expireDate = null;
/**
* @brief \IPS\nexus\Purchase|int The parent purchase or item ID
*/
public $parent = NULL;
public NexusPurchase|int|null $parent = NULL;
/**
* @brief bool Group with parent?
*/
public $groupWithParent = FALSE;
public bool $groupWithParent = FALSE;
/**
* Get Icon
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return string
* @param NexusPurchase $purchase The purchase
* @return string
*/
public static function getIcon( \IPS\nexus\Purchase $purchase )
public static function getIcon( NexusPurchase $purchase ): string
{
return static::$icon;
}
@@ -72,10 +100,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Get Title
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return string
* @param NexusPurchase $purchase The purchase
* @return string
*/
public static function getTypeTitle( \IPS\nexus\Purchase $purchase )
public static function getTypeTitle( NexusPurchase $purchase ): string
{
return static::$title;
}
@@ -83,10 +111,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Image
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return |IPS\File|NULL
* @param NexusPurchase $purchase The purchase
* @return File|null
*/
public static function purchaseImage( \IPS\nexus\Purchase $purchase )
public static function purchaseImage( NexusPurchase $purchase ): File|null
{
return NULL;
}
@@ -94,20 +122,20 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Get purchases made by a customer of this item
*
* @param \IPS\nexus\Customer $customer The customer
* @param int|array|NULL $id Item ID(s)
* @param bool $includeInactive Include expired purchases?
* @param bool $includeCanceled Include canceled purchases?
* @return \IPS\Patterns\ActiveRecordIterator
* @param Customer $customer The customer
* @param int|array|NULL $id Item ID(s)
* @param bool $includeInactive Include expired purchases?
* @param bool $includeCanceled Include canceled purchases?
* @return ActiveRecordIterator
*/
public static function getPurchases( \IPS\nexus\Customer $customer, $id = NULL, $includeInactive = TRUE, $includeCanceled = FALSE )
public static function getPurchases(Customer $customer, int|array|null $id = NULL, bool $includeInactive = TRUE, bool $includeCanceled = FALSE ): ActiveRecordIterator
{
$where = array( array( 'ps_app=? AND ps_type=? AND ps_member=?', static::$application, static::$type, $customer->member_id ) );
if ( $id !== NULL )
{
if ( \is_array( $id ) )
if ( is_array( $id ) )
{
$where[] = array( \IPS\Db::i()->in( 'ps_item_id', $id ) );
$where[] = array( Db::i()->in( 'ps_item_id', $id ) );
}
else
{
@@ -123,16 +151,16 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
$where[] = array( 'ps_cancelled=0' );
}
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_purchases', $where ), 'IPS\nexus\Purchase' );
return new ActiveRecordIterator( Db::i()->select( '*', 'nexus_purchases', $where ), 'IPS\nexus\Purchase' );
}
/**
* Get additional name info
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return array
* @param NexusPurchase $purchase The purchase
* @return array
*/
public static function getPurchaseNameInfo( \IPS\nexus\Purchase $purchase )
public static function getPurchaseNameInfo( NexusPurchase $purchase ): array
{
return array();
}
@@ -140,10 +168,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Get ACP Page HTML
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return string
* @param NexusPurchase $purchase The purchase
* @return string
*/
public static function acpPage( \IPS\nexus\Purchase $purchase )
public static function acpPage( NexusPurchase $purchase ): string
{
return '';
}
@@ -151,11 +179,11 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Get ACP Page Buttons
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param \IPS\Http\Url $url The page URL
* @return array
* @param NexusPurchase $purchase The purchase
* @param Url $url The page URL
* @return array
*/
public static function acpButtons( \IPS\nexus\Purchase $purchase, \IPS\Http\Url $url )
public static function acpButtons( NexusPurchase $purchase, Url $url ): array
{
return array();
}
@@ -163,51 +191,51 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* ACP Action
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param NexusPurchase $purchase The purchase
* @return string|null
*/
public static function acpAction( \IPS\nexus\Purchase $purchase )
public static function acpAction( NexusPurchase $purchase ): string|null
{
return null;
}
/**
* ACP Edit Form
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param \IPS\Helpers\Form $form The form
* @param \IPS\nexus\Purchase\RenewalTerm $renewals The renewal term
* @return string
* @param NexusPurchase $purchase The purchase
* @param Form $form The form
* @param PurchaseRenewalTerm|null $renewals The renewal term
* @return void
*/
public static function acpEdit( \IPS\nexus\Purchase $purchase, \IPS\Helpers\Form $form, $renewals )
public static function acpEdit(NexusPurchase $purchase, Form $form, ?PurchaseRenewalTerm $renewals ) : void
{
$form->add( new \IPS\Helpers\Form\Text( 'ps_name', $purchase->_name, TRUE, array( 'maxLength' => 128 ) ) );
$form->add( new Text( 'ps_name', $purchase->_name, TRUE, array( 'maxLength' => 128 ) ) );
if ( !$purchase->grouped_renewals and ( !$purchase->billing_agreement or $purchase->billing_agreement->canceled ) )
{
$form->add( new \IPS\Helpers\Form\Date( 'ps_expire', $purchase->expire ?: 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'does_not_expire', 'disabled' => !$purchase->canChangeExpireDate() ) ) );
$form->add( new Date( 'ps_expire', $purchase->expire ?: 0, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'does_not_expire', 'disabled' => !$purchase->canChangeExpireDate() ) ) );
}
if ( !$purchase->billing_agreement or $purchase->billing_agreement->canceled )
{
$form->add( new \IPS\nexus\Form\RenewalTerm( 'ps_renewals', $renewals, FALSE, array( 'lockTerm' => !$purchase->canChangeExpireDate() ) ) );
$form->add( new \IPS\Helpers\Form\Interval( 'ps_grace_period', $purchase->grace_period / 86400, FALSE, array( 'valueAs' => \IPS\Helpers\Form\Interval::DAYS, 'max' => \IPS\Settings::i()->cm_invoice_expireafter ?: NULL, 'min' => NULL ), NULL, NULL, NULL ) );
$form->add( new RenewalTerm( 'ps_renewals', $renewals, FALSE, array( 'lockTerm' => !$purchase->canChangeExpireDate() ) ) );
$form->add( new Interval( 'ps_grace_period', $purchase->grace_period / 86400, FALSE, array( 'valueAs' => Interval::DAYS, 'max' => Settings::i()->cm_invoice_expireafter ?: NULL, 'min' => NULL ), NULL, NULL, NULL ) );
}
if ( !$purchase->grouped_renewals )
{
$form->add( new \IPS\Helpers\Form\Node( 'ps_parent', $purchase->parent(), FALSE, array( 'class' => 'IPS\nexus\Purchase', 'forceOwner' => $purchase->member, 'zeroVal' => 'no_parent', 'disabledIds' => array( $purchase->id ) ) ) );
$form->add( new Node( 'ps_parent', $purchase->parent(), FALSE, array( 'class' => 'IPS\nexus\Purchase', 'forceOwner' => $purchase->member, 'zeroVal' => 'no_parent', 'disabledIds' => array( $purchase->id ) ) ) );
}
}
/**
* ACP Edit Save
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param NexusPurchase $purchase The purchase
* @param array $values Values from form
* @return string
* @return void
*/
public static function acpEditSave( \IPS\nexus\Purchase $purchase, array $values )
public static function acpEditSave( NexusPurchase $purchase, array $values ): void
{
$purchase->name = $values['ps_name'];
@@ -218,10 +246,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
{
if( $purchase->tax )
{
$tax = \IPS\nexus\Tax::load( $purchase->tax );
$tax = Tax::load( $purchase->tax );
}
}
catch( \OutOfRangeException $e ){}
catch( OutOfRangeException ){}
if( $tax AND $values['ps_renewals'] )
{
@@ -251,25 +279,51 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
$purchase->save();
}
}
/**
* Get Client Area Page HTML
* Additional elements that will be used to create coupons
* Also used on the Commission Rules
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return string
* @param array|string|null $current Current data
* @return array
*/
public static function clientAreaPage( \IPS\nexus\Purchase $purchase )
public static function customFormElements( array|string|null $current =null ) : array
{
return '';
return [];
}
/**
* Return an array of values that will be stored with the coupon
* Note: If you have any additional fields that have been added to the form
* but are NOT saved to the database, you MUST unset them from the values array
*
* @param array $values
* @param mixed $object The coupon or commission rule (or other object)
* @return array|null
*/
public static function saveCustomForm( array &$values=array(), mixed $object = null ) : ?array
{
return null;
}
/**
* Get Client Area Page HTML
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return string
* @param NexusPurchase $purchase The purchase
* @return array
*/
public static function clientAreaAction( \IPS\nexus\Purchase $purchase )
public static function clientAreaPage( NexusPurchase $purchase ): array
{
return array();
}
/**
* Get Client Area Page HTML
*
* @param NexusPurchase $purchase The purchase
* @return void
*/
public static function clientAreaAction( NexusPurchase $purchase ): void
{
}
@@ -277,10 +331,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Admin can change expire date / renewal term?
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return bool
* @param NexusPurchase $purchase The purchase
* @return bool
*/
public static function canChangeExpireDate( \IPS\nexus\Purchase $purchase )
public static function canChangeExpireDate( NexusPurchase $purchase ): bool
{
return TRUE;
}
@@ -288,10 +342,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Purchase can be renewed?
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return boolean
* @param NexusPurchase $purchase The purchase
* @return boolean
*/
public static function canBeRenewed( \IPS\nexus\Purchase $purchase )
public static function canBeRenewed( NexusPurchase $purchase ): bool
{
return TRUE;
}
@@ -299,56 +353,34 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* Purchase can be reactivated in the ACP?
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param NULL $error Error to show, passed by reference
* @return bool
* @param NexusPurchase $purchase The purchase
* @param string|NULL $error Error to show, passed by reference
* @return bool
*/
public static function canAcpReactivate( \IPS\nexus\Purchase $purchase, &$error=NULL )
public static function canAcpReactivate(NexusPurchase $purchase, string|null &$error=NULL ): bool
{
return TRUE;
}
/**
* Can Renew Until
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param NexusPurchase $purchase The purchase
* @param bool $admin If TRUE, is for ACP. If FALSE, is for front-end.
* @return \IPS\DateTime|bool TRUE means can renew as much as they like. FALSE means cannot renew at all. \IPS\DateTime means can renew until that date
* @return DateTime|bool TRUE means can renew as much as they like. FALSE means cannot renew at all. \IPS\DateTime means can renew until that date
*/
public static function canRenewUntil( \IPS\nexus\Purchase $purchase, $admin )
public static function canRenewUntil( NexusPurchase $purchase, bool $admin=FALSE ): DateTime|bool
{
return TRUE;
}
/**
* Get ACP Support View HTML
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return string
*/
public static function acpSupportView( \IPS\nexus\Purchase $purchase )
{
return '';
}
/**
* Support Severity
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return \IPS\nexus\Support\Severity|NULL
*/
public static function supportSeverity( \IPS\nexus\Purchase $purchase )
{
return NULL;
}
/**
* Get renewal payment methods IDs
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return array|NULL
* @param NexusPurchase $purchase The purchase
* @return array|NULL
*/
public static function renewalPaymentMethodIds( \IPS\nexus\Purchase $purchase )
public static function renewalPaymentMethodIds( NexusPurchase $purchase ): array|null
{
return NULL;
}
@@ -356,11 +388,11 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Purchase Generated
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param \IPS\nexus\Invoice $invoice The invoice
* @return void
* @param NexusPurchase $purchase The purchase
* @param Invoice $invoice The invoice
* @return void
*/
public static function onPurchaseGenerated( \IPS\nexus\Purchase $purchase, \IPS\nexus\Invoice $invoice )
public static function onPurchaseGenerated( NexusPurchase $purchase, Invoice $invoice ): void
{
}
@@ -368,11 +400,11 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Renew (Renewal invoice paid. Is not called if expiry data is manually changed)
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param int $cycles Cycles
* @return void
* @param NexusPurchase $purchase The purchase
* @param int $cycles Cycles
* @return void
*/
public static function onRenew( \IPS\nexus\Purchase $purchase, $cycles )
public static function onRenew(NexusPurchase $purchase, int $cycles = 1): void
{
}
@@ -380,10 +412,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Expiration Date Change
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param NexusPurchase $purchase The purchase
* @return void
*/
public static function onExpirationDateChange( \IPS\nexus\Purchase $purchase )
public static function onExpirationDateChange( NexusPurchase $purchase ): void
{
}
@@ -392,10 +424,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
* On expire soon
* If returns TRUE, the normal expire warning email will not be sent
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param NexusPurchase $purchase The purchase
* @return bool
*/
public static function onExpireWarning( \IPS\nexus\Purchase $purchase )
public static function onExpireWarning( NexusPurchase $purchase ): bool
{
return FALSE;
}
@@ -403,10 +435,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Purchase Expired
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param NexusPurchase $purchase The purchase
* @return void
*/
public static function onExpire( \IPS\nexus\Purchase $purchase )
public static function onExpire( NexusPurchase $purchase ): void
{
}
@@ -414,21 +446,21 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Purchase Canceled
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param NexusPurchase $purchase The purchase
* @return void
*/
public static function onCancel( \IPS\nexus\Purchase $purchase )
public static function onCancel( NexusPurchase $purchase ): void
{
}
/**
* Warning to display to admin when cancelling a purchase
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @return string
* @param NexusPurchase $purchase
* @return string|null
*/
public static function onCancelWarning( \IPS\nexus\Purchase $purchase )
public static function onCancelWarning( NexusPurchase $purchase ): string|null
{
return NULL;
}
@@ -436,10 +468,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Purchase Deleted
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param NexusPurchase $purchase The purchase
* @return void
*/
public static function onDelete( \IPS\nexus\Purchase $purchase )
public static function onDelete( NexusPurchase $purchase ): void
{
}
@@ -447,10 +479,10 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Purchase Reactivated (renewed after being expired or reactivated after being canceled)
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @return void
* @param NexusPurchase $purchase The purchase
* @return void
*/
public static function onReactivate( \IPS\nexus\Purchase $purchase )
public static function onReactivate( NexusPurchase $purchase ): void
{
}
@@ -458,11 +490,11 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
/**
* On Transfer (is ran before transferring)
*
* @param \IPS\nexus\Purchase $purchase The purchase
* @param \IPS\Member $newCustomer New Customer
* @return void
* @param NexusPurchase $purchase The purchase
* @param Member $newCustomer New Customer
* @return void
*/
public static function onTransfer( \IPS\nexus\Purchase $purchase, \IPS\Member $newCustomer )
public static function onTransfer( NexusPurchase $purchase, Member $newCustomer ): void
{
}
@@ -471,19 +503,19 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
* Requires Billing Address
*
* @return bool
* @throws \DomainException
* @throws DomainException
*/
public function requiresBillingAddress()
public function requiresBillingAddress(): bool
{
return \in_array( 'product', explode( ',', \IPS\Settings::i()->nexus_require_billing ) );
return in_array( 'product', explode( ',', Settings::i()->nexus_require_billing ) );
}
/**
* Show Purchase Record?
*
* @return bool
* @return bool
*/
public function showPurchaseRecord()
public function showPurchaseRecord(): bool
{
return TRUE;
}
@@ -492,15 +524,15 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
* Is this item the same as another item in the cart?
* Used to decide when an item is added to the cart if we should just increase the quantity of this item instead of creating a new item.
*
* @param \IPS\nexus\Invoice\Item $item The other item
* @return bool
* @param Item $item The other item
* @return bool
*/
public function isSameAsOtherItem( $item )
public function isSameAsOtherItem( Item $item ): bool
{
// You can't compare DateInterval objects, it just throws an exception, so we have to
// manually figure out if the renewal terms have any differences
if ( $item instanceof \IPS\nexus\Invoice\Item\Purchase )
if ( $item instanceof static )
{
if ( $item->renewalTerm xor $this->renewalTerm )
{
@@ -518,7 +550,7 @@ abstract class _Purchase extends \IPS\nexus\Invoice\Item
{
return FALSE;
}
if ( $item->renewalTerm->cost != $item->renewalTerm->cost )
if ( $item->renewalTerm->cost != $this->renewalTerm->cost )
{
return FALSE;
}
@@ -12,50 +12,74 @@
namespace IPS\nexus\Invoice\Item;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use DateInterval;
use Exception;
use IPS\Application;
use IPS\DateTime;
use IPS\File;
use IPS\Http\Url;
use IPS\Member;
use IPS\nexus\Coupon;
use IPS\nexus\Customer;
use IPS\nexus\Invoice;
use IPS\nexus\Invoice\Item;
use IPS\nexus\Money;
use IPS\nexus\Purchase;
use IPS\nexus\Purchase\RenewalTerm;
use IPS\nexus\Transaction;
use OutOfRangeException;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invoice Item Class for Renewals
*/
class _Renewal extends \IPS\nexus\Invoice\Item
class Renewal extends Item
{
/**
* @brief Act (new/charge)
*/
public static $act = 'renewal';
public static string $act = 'renewal';
/**
* @brief Icon
*/
public static $icon = 'refresh';
public static string $icon = 'arrows-rotate';
/**
* @brief Title
*/
public static $title = 'renewal';
public static string $title = 'renewal';
/**
* @brief Requires login to purchase?
*/
public static $requiresAccount = TRUE;
public static bool $requiresAccount = TRUE;
/**
* @brief New expiry date (NULL will cause automatic calculation)
*/
public $expire = NULL;
public mixed $expire = NULL;
/**
* @brief Expiry date (NULL will cause automatic calculation)
*/
public mixed $expireDate = NULL;
/**
* Create
*
* @param \IPS\nexus\Purchase $purchase The purchase to renew
* @param int $cycles The number of cycles to renew for
* @return void
* @param Purchase $purchase The purchase to renew
* @param int $cycles The number of cycles to renew for
* @return static
*/
public static function create( \IPS\nexus\Purchase $purchase, $cycles = 1 )
public static function create( Purchase $purchase, int $cycles = 1 ): static
{
$obj = new static( sprintf( $purchase->member->language()->get('renew_title'), $purchase->name ), $purchase->renewals->cost );
$obj->tax = $purchase->renewals->tax;
@@ -78,23 +102,75 @@ class _Renewal extends \IPS\nexus\Invoice\Item
return $obj;
}
/**
* Used to override the static property, if necessary
*
* @return bool
*/
public function canUseAccountCredit() : bool
{
/* Account credits can only be used for renewals if the original item
is allowed to use credit */
if( $extension = $this->getPurchaseExtension() )
{
return $extension->canUseAccountCredit();
}
return static::$canUseAccountCredit;
}
/**
* Used to override the static property, if necessary
*
* @return bool
*/
public function canUseCoupons() : bool
{
/* Coupons can only be used on renewals for packages that allow coupons */
if( $extension = $this->getPurchaseExtension() )
{
return $extension->canUseCoupons();
}
return static::$canUseCoupons;
}
/**
* Determines if the coupon can be applied to this item
*
* @param array|string $data
* @param Invoice $invoice
* @param Customer $customer
* @return bool
*/
public function isValid( array|string $data, Invoice $invoice, Customer $customer ) : bool
{
/* Coupons can only be used on renewals for packages that allow coupons */
if( $extension = $this->getPurchaseExtension() )
{
return $extension->isValid( $data, $invoice, $customer );
}
return false;
}
/**
* On Paid
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @return void
* @param Invoice $invoice The invoice
* @return void
*/
public function onPaid( \IPS\nexus\Invoice $invoice )
public function onPaid( Invoice $invoice ): void
{
$purchase = \IPS\nexus\Purchase::load( $this->id );
$purchase = Purchase::load( $this->id );
if ( $purchase->renewals and $interval = $purchase->renewals->interval and !$purchase->cancelled and $expire = $purchase->expire )
{
$_expire = clone $expire;
if ( $_expire->add( new \DateInterval( 'PT' . $purchase->grace_period . 'S' ) )->getTimestamp() < time() )
if ( $_expire->add( new DateInterval( 'PT' . $purchase->grace_period . 'S' ) )->getTimestamp() < time() )
{
$expire = new \IPS\DateTime;
$expire = new DateTime;
}
for ( $i=0; $i<$this->quantity; $i++ )
{
@@ -105,7 +181,7 @@ class _Renewal extends \IPS\nexus\Invoice\Item
$purchase->invoice_pending = NULL;
$billingAgreement = NULL;
foreach ( $invoice->transactions( array( \IPS\nexus\Transaction::STATUS_PAID, \IPS\nexus\Transaction::STATUS_PART_REFUNDED ), array( array( 't_billing_agreement IS NOT NULL' ) ) ) as $transaction )
foreach ( $invoice->transactions( array( Transaction::STATUS_PAID, Transaction::STATUS_PART_REFUNDED ), array( array( 't_billing_agreement IS NOT NULL' ) ) ) as $transaction )
{
$billingAgreement = $transaction->billing_agreement;
}
@@ -115,7 +191,7 @@ class _Renewal extends \IPS\nexus\Invoice\Item
}
$purchase->save();
$purchase->onRenew( $this->quantity );
$purchase->onRenew($this->quantity);
$purchase->member->log( 'purchase', array( 'type' => 'renew', 'id' => $purchase->id, 'name' => $purchase->name, 'invoice_id' => $invoice->id, 'invoice_title' => $invoice->title ) );
}
@@ -124,18 +200,18 @@ class _Renewal extends \IPS\nexus\Invoice\Item
/**
* On Unpaid description
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @param Invoice $invoice The invoice
* @return array
*/
public function onUnpaidDescription( \IPS\nexus\Invoice $invoice )
public function onUnpaidDescription( Invoice $invoice ): array
{
$return = parent::onUnpaidDescription( $invoice );
try
{
$purchase = \IPS\nexus\Purchase::load( $this->id );
$purchase = Purchase::load( $this->id );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException )
{
return $return;
}
@@ -147,7 +223,7 @@ class _Renewal extends \IPS\nexus\Invoice\Item
$expire->sub( $interval );
}
$return[] = \IPS\Member::loggedIn()->language()->addToStack( 'renewal_unpaid', FALSE, array( 'sprintf' => array( $purchase->name, $purchase->id, $expire->localeDate() ) ) );
$return[] = Member::loggedIn()->language()->addToStack( 'renewal_unpaid', FALSE, array( 'sprintf' => array( $purchase->name, $purchase->id, $expire->localeDate() ) ) );
}
return $return;
@@ -156,17 +232,17 @@ class _Renewal extends \IPS\nexus\Invoice\Item
/**
* On Unpaid
*
* @param \IPS\nexus\Invoice $invoice The invoice
* @param Invoice $invoice The invoice
* @param string $status Status
* @return void
* @return void
*/
public function onUnpaid( \IPS\nexus\Invoice $invoice, $status )
public function onUnpaid( Invoice $invoice, string $status ): void
{
try
{
$purchase = \IPS\nexus\Purchase::load( $this->id );
$purchase = Purchase::load( $this->id );
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException )
{
return;
}
@@ -188,15 +264,15 @@ class _Renewal extends \IPS\nexus\Invoice\Item
/**
* Client Area URL
*
* @return |IPS\Http\Url|NULL
* @return Url|string|null
*/
public function url()
function url(): Url|string|null
{
try
{
return \IPS\nexus\Purchase::load( $this->id )->url();
return Purchase::load( $this->id )->url();
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException )
{
return NULL;
}
@@ -205,15 +281,15 @@ class _Renewal extends \IPS\nexus\Invoice\Item
/**
* ACP URL
*
* @return |IPS\Http\Url|NULL
* @return Url|null
*/
public function acpUrl()
public function acpUrl(): Url|null
{
try
{
return \IPS\nexus\Purchase::load( $this->id )->acpUrl();
return Purchase::load( $this->id )->acpUrl();
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException )
{
return NULL;
}
@@ -222,15 +298,15 @@ class _Renewal extends \IPS\nexus\Invoice\Item
/**
* Image
*
* @return |IPS\File|NULL
* @return File|null
*/
public function image()
public function image(): File|null
{
try
{
return \IPS\nexus\Purchase::load( $this->id )->image();
return Purchase::load( $this->id )->image();
}
catch ( \OutOfRangeException $e )
catch ( OutOfRangeException )
{
return NULL;
}
@@ -239,17 +315,44 @@ class _Renewal extends \IPS\nexus\Invoice\Item
/**
* Utility method to get the purchase. Useful if we are unsure if the purchase exists, or has been removed due to a refund of the original transaction.
*
* @return \IPS\nexus\Purchase|NULL
* @return Purchase|NULL
*/
public function getPurchase()
public function getPurchase() : Purchase|null
{
try
{
return \IPS\nexus\Purchase::load( $this->id );
return Purchase::load( $this->id );
}
catch( \OutOfRangeException $e )
catch( OutOfRangeException )
{
return NULL;
}
}
public function getPurchaseExtension() : Item|null
{
if( $purchase = $this->getPurchase() )
{
/* Copy the code to load the extension. It's a protected method and I don't really want to
expose it */
try
{
foreach ( Application::load( $purchase->app )->extensions( 'nexus', 'Item', FALSE ) as $ext )
{
if ( $ext::$type == $purchase->type )
{
$price = new Money( "0", $purchase->member->defaultCurrency() );
if( $purchase->renewals instanceof RenewalTerm )
{
$price = $purchase->renewals->cost;
}
return new $ext( $purchase->name, $price );
}
}
}
catch( Exception ){}
}
return null;
}
}
@@ -12,26 +12,31 @@
namespace IPS\nexus\Invoice\Item;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use IPS\Member;
use IPS\nexus\Tax;
use function defined;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Tax Item
*/
class _TaxItem
class TaxItem
{
/**
* @brief Class ID
*/
protected $class;
protected int $class;
/**
* @brief Data
*/
protected $data;
protected array $data;
/**
* Constructor
@@ -40,7 +45,7 @@ class _TaxItem
* @param array $data Data
* @return void
*/
public function __construct( $class, $data )
public function __construct( int $class, array $data )
{
$this->class = $class;
$this->data = $data;
@@ -49,16 +54,16 @@ class _TaxItem
/**
* Get output for API
*
* @param \IPS\Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @param Member|NULL $authorizedMember The member making the API request or NULL for API Key / client_credentials
* @return array
* @apiresponse \IPS\nexus\Tax class The tax class
* @apiresponse float rate The rate (for example 0.2 means 20%)
* @apiresponse \IPS\nexus\Money amount Amount to pay
*/
public function apiOutput( \IPS\Member $authorizedMember = NULL )
public function apiOutput( Member $authorizedMember = NULL ): array
{
return array(
'class' => \IPS\nexus\Tax::load( $this->class )->apiOutput( $authorizedMember ),
'class' => Tax::load( $this->class )->apiOutput( $authorizedMember ),
'rate' => $this->data['rate'],
'amount' => $this->data['amount']
);
@@ -12,36 +12,56 @@
namespace IPS\nexus\Invoice;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
use ArrayIterator;
use DateInterval;
use Exception;
use IPS\Application;
use IPS\DateTime;
use IPS\Http\Url;
use IPS\nexus\Customer;
use IPS\nexus\Invoice\Item\Renewal;
use IPS\nexus\Money;
use IPS\nexus\Package\CustomField;
use IPS\nexus\Purchase;
use IPS\nexus\Purchase\RenewalTerm;
use IPS\nexus\Tax;
use IPS\Text\Encrypt;
use OutOfRangeException;
use function defined;
use function in_array;
use function is_object;
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invoice Items Iterator
*/
class _ItemsIterator extends \ArrayIterator
class ItemsIterator extends ArrayIterator
{
/**
* @brief Currency
*/
public $currency;
public string $currency;
/**
* @brief Class Names
*/
protected static $classnames = NULL;
protected static ?array $classnames = NULL;
/**
* Convert array into object
*
* @param array $data Data
* @return \IPS\nexus\Invoice\Item
* @param array|Item $data Data
* @return Item
*/
public function arrayToObject( $data )
public function arrayToObject( array|Item $data ) : Item
{
if ( \is_object( $data ) )
if ( is_object( $data ) )
{
return $data;
}
@@ -49,29 +69,29 @@ class _ItemsIterator extends \ArrayIterator
/* Load correct class */
if ( $data['act'] === 'renewal' )
{
$obj = new \IPS\nexus\Invoice\Item\Renewal( $data['itemName'], new \IPS\nexus\Money( $data['cost'], $this->currency ) );
$obj = new Renewal( $data['itemName'], new Money( $data['cost'], $this->currency ) );
}
else
{
if ( static::$classnames === NULL )
{
static::$classnames = array();
foreach ( \IPS\Application::allExtensions( 'nexus', 'Item', FALSE, NULL, NULL, FALSE ) as $ext )
foreach ( Application::allExtensions( 'nexus', 'Item', FALSE, NULL, NULL, FALSE ) as $ext )
{
static::$classnames[ $ext::$application ][ $ext::$type ] = $ext;
}
}
if ( $data['app'] === 'nexus' and \in_array( $data['type'], array( 'product', 'ad' ) ) )
if ( $data['app'] === 'nexus' and in_array( $data['type'], array( 'product', 'ad' ) ) )
{
$data['type'] = 'package';
}
$class = isset( static::$classnames[ $data['app'] ][ $data['type'] ] ) ? static::$classnames[ $data['app'] ][ $data['type'] ] : NULL;
$class = static::$classnames[$data['app']][$data['type']] ?? NULL;
if ( !$class )
{
$class = 'IPS\nexus\extensions\nexus\Item\MiscellaneousCharge';
}
$obj = new $class( $data['itemName'], new \IPS\nexus\Money( number_format( $data['cost'], \IPS\nexus\Money::numberOfDecimalsForCurrency( $this->currency ), '.', '' ), $this->currency ) );
$obj = new $class( $data['itemName'], new Money( number_format( $data['cost'], Money::numberOfDecimalsForCurrency( $this->currency ), '.', '' ), $this->currency ) );
}
/* Basic information */
@@ -90,13 +110,13 @@ class _ItemsIterator extends \ArrayIterator
$purchaseDetails[ $key ] = $value;
try
{
$field = \IPS\nexus\Package\CustomField::load( $key );
$field = CustomField::load( $key );
if ( $field->invoice )
{
switch( $field->type )
{
case 'UserPass':
$value = json_decode( \IPS\Text\Encrypt::fromTag( $value )->decrypt(), TRUE );
$value = json_decode( Encrypt::fromTag( $value )->decrypt(), TRUE );
$display = array();
if ( $value['un'] )
{
@@ -112,8 +132,8 @@ class _ItemsIterator extends \ArrayIterator
break;
case 'Ftp':
$value = json_decode( \IPS\Text\Encrypt::fromTag( $value )->decrypt(), TRUE );
$details[ $key ] = (string) \IPS\Http\Url::createFromComponents(
$value = json_decode( Encrypt::fromTag( $value )->decrypt(), TRUE );
$details[ $key ] = (string) Url::createFromComponents(
$value['server'],
( isset( $value['protocol'] ) ) ? "{$value['protocol']}://" : 'ftp://',
( isset( $value['path'] ) ) ? $value['path'] : NULL,
@@ -130,7 +150,7 @@ class _ItemsIterator extends \ArrayIterator
}
}
}
catch( \OutOfRangeException $e ) {}
catch( OutOfRangeException ) {}
}
}
@@ -142,25 +162,25 @@ class _ItemsIterator extends \ArrayIterator
{
try
{
$obj->tax = \IPS\nexus\Tax::load( $data['tax'] );
$obj->tax = Tax::load( $data['tax'] );
}
catch ( \Exception $e ) { }
catch ( Exception ) { }
}
/* Renewal terms */
if ( isset( $data['renew_term'] ) and $data['renew_term'] )
{
$obj->renewalTerm = new \IPS\nexus\Purchase\RenewalTerm( new \IPS\nexus\Money( $data['renew_cost'], $this->currency ), new \DateInterval( 'P' . $data['renew_term'] . mb_strtoupper( $data['renew_units'] ) ), $obj->tax, FALSE, isset( $data['grace_period'] ) ? new \DateInterval( 'PT' . $data['grace_period'] . 'S' ) : NULL );
$obj->renewalTerm = new RenewalTerm( new Money( $data['renew_cost'], $this->currency ), new DateInterval( 'P' . $data['renew_term'] . mb_strtoupper( $data['renew_units'] ) ), $obj->tax, FALSE, isset( $data['grace_period'] ) ? new DateInterval( 'PT' . $data['grace_period'] . 'S' ) : NULL );
if ( isset( $data['initial_interval_term'] ) and $data['initial_interval_term'] )
{
$obj->initialInterval = new \DateInterval( 'P' . $data['initial_interval_term'] . mb_strtoupper( $data['initial_interval_unit'] ) );
$obj->initialInterval = new DateInterval( 'P' . $data['initial_interval_term'] . mb_strtoupper( $data['initial_interval_unit'] ) );
}
}
/* Expire Date */
if ( isset( $data['expires'] ) and $data['expires'] )
{
$obj->expireDate = \IPS\DateTime::ts( $data['expires'] );
$obj->expireDate = DateTime::ts( $data['expires'] );
}
/* Available methods */
@@ -169,25 +189,6 @@ class _ItemsIterator extends \ArrayIterator
$obj->paymentMethodIds = $data['methods'];
}
/* Shipping */
if ( isset( $data['physical'] ) AND $data['physical'] )
{
$obj->physical = TRUE;
if ( isset( $data['shipping'] ) and $data['shipping'] !== '*' )
{
$obj->shippingMethodIds = $data['shipping'];
}
$obj->weight = new \IPS\nexus\Shipping\Weight( $data['weight'] );
$obj->length = new \IPS\nexus\Shipping\Length( $data['length'] );
$obj->width = new \IPS\nexus\Shipping\Length( $data['width'] );
$obj->height = new \IPS\nexus\Shipping\Length( $data['height'] );
if ( isset( $data['chosen_shipping'] ) and $data['chosen_shipping'] )
{
$obj->chosenShippingMethodId = $data['chosen_shipping'];
}
}
/* Parent */
if ( isset( $data['assoc'] ) )
{
@@ -195,16 +196,16 @@ class _ItemsIterator extends \ArrayIterator
{
try
{
$obj->parent = \IPS\nexus\Purchase::load( $data['assoc'] );
$obj->parent = Purchase::load( $data['assoc'] );
}
catch ( \OutOfRangeException $e ) { }
catch ( OutOfRangeException ) { }
}
else
{
$obj->parent = $data['assoc'];
}
$obj->groupWithParent = isset( $data['groupParent'] ) ? $data['groupParent'] : FALSE;
$obj->groupWithParent = $data['groupParent'] ?? FALSE;
}
/* Paying another member? */
@@ -212,17 +213,17 @@ class _ItemsIterator extends \ArrayIterator
{
try
{
$obj->payTo = \IPS\nexus\Customer::load( $data['payTo'] );
$obj->payTo = Customer::load( $data['payTo'] );
if ( isset( $data['commission'] ) )
{
$obj->commission = ( $data['commission'] <= 100 ) ? $data['commission'] : 100;
}
if ( isset( $data['fee'] ) )
{
$obj->fee = new \IPS\nexus\Money( $data['fee'], $this->currency );
$obj->fee = new Money( $data['fee'], $this->currency );
}
}
catch ( \Exception $e ) { }
catch ( Exception ) { }
}
/* URIs? */
@@ -247,9 +248,9 @@ class _ItemsIterator extends \ArrayIterator
/**
* Get current
*
* @return \IPS\Patterns\ActiveRecord
* @return Item
*/
public function current()
public function current(): Item
{
return $this->arrayToObject( parent::current() );
}
@@ -257,11 +258,11 @@ class _ItemsIterator extends \ArrayIterator
/**
* Get offset
*
* @param string $index Index
* @return mixed
* @param mixed $key Index
* @return Item
*/
public function offsetGet( $index )
public function offsetGet( mixed $key ): Item
{
return $this->arrayToObject( parent::offsetGet( $index ) );
return $this->arrayToObject( parent::offsetGet( $key ) );
}
}