Files
IPB/applications/nexus/sources/Invoice/Invoice.php
T
2025-12-19 05:32:19 -08:00

1899 lines
51 KiB
PHP

<?php
/**
* @brief Invoice Model
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @subpackage Nexus
* @since 10 Feb 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\nexus;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/**
* Invoice Model
*/
class _Invoice extends \IPS\Patterns\ActiveRecord
{
const STATUS_PAID = 'paid';
const STATUS_PENDING = 'pend';
const STATUS_EXPIRED = 'expd';
const STATUS_CANCELED = 'canc';
/**
* @brief Multiton Store
*/
protected static $multitons;
/**
* @brief Database Table
*/
public static $databaseTable = 'nexus_invoices';
/**
* @brief Database Prefix
*/
public static $databasePrefix = 'i_';
/**
* Load and check permissions
*
* @return \IPS\Content\Item
* @throws \OutOfRangeException
*/
public static function loadAndCheckPerms( $id )
{
$obj = static::load( $id );
if ( !$obj->canView() )
{
throw new \OutOfRangeException;
}
return $obj;
}
/**
* Get statuses
*
* @return array
*/
public static function statuses()
{
$options = array();
$reflection = new \ReflectionClass( get_called_class() );
foreach ( $reflection->getConstants() as $k => $v )
{
if ( mb_substr( $k, 0, 7 ) === 'STATUS_' )
{
$options[ $v ] = "istatus_{$v}";
}
}
return $options;
}
/**
* Get invoices table
*
* @param array $where Where clause
* @param string $ref Referer
* @return \IPS\Helpers\Table\Db
*/
public static function table( $where = array(), \IPS\Http\Url $url, $ref = 't' )
{
/* Create the table */
$table = new \IPS\Helpers\Table\Db( 'nexus_invoices', $url, $where );
$table->sortBy = $table->sortBy ?: 'i_date';
/* Format Columns */
$table->include = array( 'i_status', 'i_id', 'i_title', 'i_member', 'i_total', 'i_date' );
$table->parsers = array(
'i_status' => function( $val )
{
return \IPS\Theme::i()->getTemplate('invoices')->status( $val );
},
'i_member' => function ( $val, $row )
{
if ( $val )
{
return \IPS\Theme::i()->getTemplate('global')->userLink( \IPS\Member::load( $val ) );
}
elseif ( $row['i_guest_data'] )
{
$data = json_decode( $row['i_guest_data'], TRUE );
return \IPS\nexus\Customer::constructFromData( $data['member'] )->cm_name;
}
else
{
return \IPS\Theme::i()->getTemplate('global')->userLink( \IPS\Member::load( 0 ) );
}
},
'i_total' => function( $val, $row )
{
return (string) new \IPS\nexus\Money( $val, $row['i_currency'] );
},
'i_date' => function( $val )
{
return \IPS\DateTime::ts( $val );
}
);
/* Buttons */
$table->rowButtons = function( $row ) use( $ref )
{
return array_merge( array(
'view' => array(
'icon' => 'search',
'title' => 'invoice_view',
'link' => \IPS\Http\Url::internal( "app=nexus&module=payments&controller=invoices&do=view&id={$row['i_id']}" )
),
), \IPS\nexus\Invoice::constructFromData( $row )->buttons( $ref ) );
};
return $table;
}
/**
* @brief Being generated by system?
*/
public $system = FALSE;
/**
* Set Default Values
*
* @return void
*/
public function setDefaultValues()
{
$this->status = static::STATUS_PENDING;
$this->items = json_encode( array() );
$this->date = new \IPS\DateTime;
$this->renewal_ids = array();
$this->guest_data = array();
$this->_data['total'] = 0;
$this->_data['member'] = 0;
$this->status_extra = array();
}
/**
* Member can view?
*
* @param \IPS\Member|NULL $member The member to check for, or NULL for currently logged in member
* @return bool
*/
public function canView( \IPS\Member $member = NULL )
{
$member = $member ?: \IPS\Member::loggedIn();
return $this->member->member_id === $member->member_id or array_key_exists( $member->member_id, iterator_to_array( $this->member->alternativeContacts( array( 'billing=1' ) ) ) );
}
/**
* Set title
*
* @param string
* @return void
* @throws \LengthException
*/
public function set_title( $title )
{
if ( mb_strlen( $title ) > 128 )
{
throw new \LengthException;
}
$this->_data['title'] = $title;
}
/**
* @brief Member
*/
protected $_member;
/**
* Get member
*
* @return \IPS\Member
*/
public function get_member()
{
if ( $this->_member === NULL )
{
try
{
$this->_member = \IPS\nexus\Customer::load( $this->_data['member'] );
}
catch ( \OutOfRangeException $e )
{
$this->_member = new \IPS\nexus\Customer;
if ( $this->_data['member'] )
{
$this->_member->_data['cm_first_name'] = \IPS\Member::loggedIn()->language()->addToStack('deleted_member');
}
elseif ( $this->guest_data )
{
foreach ( $this->guest_data['member'] as $k => $v )
{
$this->_member->_data[ $k ] = $v;
$this->_member->changed[ $k ] = $v;
}
}
}
}
return $this->_member;
}
/**
* Set member
*
* @param \IPS\Member
* @return void
*/
public function set_member( \IPS\Member $member )
{
$this->_data['member'] = (int) $member->member_id;
$this->_member = NULL;
if ( !$this->currency and $member instanceof \IPS\nexus\Customer )
{
$this->currency = $member->defaultCurrency();
}
}
/**
* @brief Items iterator
*/
protected $_itemsIterator;
/**
* Get items
*
* @return array
*/
public function get_items()
{
if ( $this->_itemsIterator === NULL )
{
/* Decode */
$itemsJson = json_decode( $this->_data['items'], TRUE ) ?: array();
/* Check we actually have a valid array with items (very old invoices from IP.Subscriptions may not have anything) and
just create a dummy if so */
$itemsJson = array_filter( $itemsJson, function( $val )
{
return is_array( $val ) and isset( $val['act'] );
} );
if ( !$itemsJson and $this->total->amount )
{
$itemsJson = array( array(
'act' => 'charge',
'app' => 'nexus',
'type' => 'charge',
'cost' => $this->total->amount,
'tax' => NULL,
'quantity' => 1,
'itemName' => $this->title,
'itemID' => NULL,
'physical' => FALSE,
'methods' => '*',
'cfields' => array(),
'extra' => NULL,
) );
}
/* Return it */
$this->_itemsIterator = new \IPS\nexus\Invoice\ItemsIterator( $itemsJson );
$this->_itemsIterator->currency = $this->currency;
}
return $this->_itemsIterator;
}
/**
* Get total
*
* @return \IPS\nexus\Money
*/
public function get_total()
{
return new \IPS\nexus\Money( $this->_data['total'], $this->currency );
}
/**
* Set total
*
* @param \IPS\nexus\Money $total The total
* @return void
*/
public function set_total( \IPS\nexus\Money $total )
{
$this->_data['total'] = $total->amount;
}
/**
* Get date
*
* @return \IPS\DateTime
*/
public function get_date()
{
return \IPS\DateTime::ts( $this->_data['date'] );
}
/**
* Set date
*
* @param \IPS\DateTime $date The invoice date
* @return void
*/
public function set_date( \IPS\DateTime $date )
{
$this->_data['date'] = $date->getTimestamp();
}
/**
* Get paid date date
*
* @return \IPS\DateTime|NULL
*/
public function get_paid()
{
return $this->_data['paid'] ? \IPS\DateTime::ts( $this->_data['paid'] ) : NULL;
}
/**
* Set paid date
*
* @param \IPS\DateTime $date The invoice date
* @return void
*/
public function set_paid( \IPS\DateTime $date )
{
$this->_data['paid'] = $date->getTimestamp();
}
/**
* Get return URI
*
* @return \IPS\Http\Url|NULL
*/
public function get_return_uri()
{
return $this->_data['return_uri'] ? \IPS\Http\Url::internal( $this->_data['return_uri'] ) : NULL;
}
/**
* Get status information
*
* @return mixed
*/
public function get_status_extra()
{
return json_decode( $this->_data['status_extra'], TRUE );
}
/**
* Set status information
*
* @param mixed $extra The data
* @return void
*/
public function set_status_extra( $extra )
{
$this->_data['status_extra'] = json_encode( $extra );
}
/**
* Get renewal IDs
*
* @return array
*/
public function get_renewal_ids()
{
return explode( ',', $this->_data['renewal_ids'] );
}
/**
* Set renewal IDs
*
* @param array $ids The IDs
* @return void
*/
public function set_renewal_ids( $ids )
{
array_unique( $ids );
$ids = array_filter( $ids );
$this->_data['renewal_ids'] = implode( ',', $ids );
}
/**
* Get shipping address
*
* @return \IPS\GeoLocation|NULL
*/
public function get_shipaddress()
{
if ( !$this->hasPhysicalItems() )
{
return NULL;
}
if ( isset( $this->_data['shipaddress'] ) and $this->_data['shipaddress'] )
{
return \IPS\GeoLocation::buildFromJson( $this->_data['shipaddress'] );
}
else
{
try
{
return \IPS\GeoLocation::buildFromJson( \IPS\Db::i()->select( 'address', 'nexus_customer_addresses', array( 'member=? AND primary_shipping=1', $this->member->member_id ) )->first() );
}
catch ( \UnderflowException $e )
{
return NULL;
}
}
}
/**
* Set shipping address
*
* @param \IPS\GeoLocation $shippingAddress The shipping address
* @return void
*/
public function set_shipaddress( \IPS\GeoLocation $shippingAddress )
{
$this->_data['shipaddress'] = json_encode( $shippingAddress );
}
/**
* Get billing address
*
* @return \IPS\GeoLocation|NULL
*/
public function get_billaddress()
{
if ( isset( $this->_data['billaddress'] ) and $this->_data['billaddress'] )
{
return \IPS\GeoLocation::buildFromJson( $this->_data['billaddress'] );
}
else
{
try
{
return \IPS\GeoLocation::buildFromJson( \IPS\Db::i()->select( 'address', 'nexus_customer_addresses', array( 'member=? AND primary_billing=1', $this->member->member_id ) )->first() );
}
catch ( \UnderflowException $e )
{
return NULL;
}
}
}
/**
* Set billing address
*
* @param \IPS\GeoLocation $billingAddress The billing address
* @return void
*/
public function set_billaddress( \IPS\GeoLocation $billingAddress )
{
$this->_data['billaddress'] = json_encode( $billingAddress );
$this->_data['billcountry'] = $billingAddress->country;
}
/**
* Get guest data
*
* @return mixed
*/
public function get_guest_data()
{
return $this->_data['guest_data'] ? json_decode( $this->_data['guest_data'], TRUE ) : NULL;
}
/**
* Set guest data
*
* @param mixed $data The data
* @return void
*/
public function set_guest_data( $data )
{
$this->_data['guest_data'] = $data ? json_encode( $data ) : NULL;
}
/**
* Save Changed Columns
*
* @return void
*/
public function save()
{
if ( !$this->title )
{
$titles = array();
foreach ( $this->items as $item )
{
$titles[] = ( $item->name . ( $item->quantity > 1 ? " x{$item->quantity}" : '' ) );
}
$thisTitle = implode( ', ', $titles );
if ( mb_strlen( $thisTitle ) > 128 )
{
$thisTitle = mb_substr( $thisTitle, 0, 125 ) . '...';
}
$this->title = $thisTitle;
}
if ( $this->_new )
{
parent::save();
$this->member->log( 'invoice', array( 'id' => $this->id, 'title' => $this->title, 'status' => $this->status, 'system' => $this->system ), $this->system ? FALSE : NULL );
}
else
{
parent::save();
}
}
/**
* Delete
*
* @return void
*/
public function delete()
{
parent::delete();
foreach ( $this->transactions( array( \IPS\nexus\Transaction::STATUS_PENDING, \IPS\nexus\Transaction::STATUS_WAITING, \IPS\nexus\Transaction::STATUS_HELD, \IPS\nexus\Transaction::STATUS_REVIEW ) ) as $transaction )
{
if ( $transaction->status === \IPS\nexus\Transaction::STATUS_PENDING )
{
$transaction->delete();
}
else
{
try
{
$transaction->method->void( $transaction );
}
catch ( \Exception $e ){}
$extra = $transaction->extra;
$extra['history'][] = array( 's' => \IPS\nexus\Transaction::STATUS_REFUSED, 'on' => time(), 'note' => 'invoiceDeleted' );
$transaction->extra = $extra;
$transaction->status = \IPS\nexus\Transaction::STATUS_REFUSED;
$transaction->save();
}
}
}
/* !Items */
/**
* Add item
*
* @param \IPS\nexus\Invoice\Item $item The item
* @return int
*/
public function addItem( \IPS\nexus\Invoice\Item $item )
{
/* Set basic data */
$data = array(
'act' => $item::$act,
'app' => isset( $item::$application ) ? $item::$application : $item->application,
'type' => isset( $item::$type ) ? $item::$type : $item->type,
'cost' => $item->price->amount,
'tax' => $item->tax ? $item->tax->id : NULL,
'quantity' => $item->quantity,
'itemName' => $item->name,
'itemID' => $item->id,
'physical' => FALSE,
'methods' => $item->paymentMethodIds ?: '*',
'cfields' => $item->details,
'extra' => $item->extra,
);
/* Parent? */
if ( isset( $item->parent ) and $item->parent !== NULL )
{
if ( is_int( $item->parent ) )
{
$data['assoc'] = $item->parent;
$data['assocBought'] = FALSE;
}
else
{
$data['assoc'] = $item->parent->id;
$data['assocBought'] = TRUE;
}
$data['groupParent'] = $item->groupWithParent;
}
/* Renewal term? */
if ( isset( $item->renewalTerm ) )
{
$renewTermData = $item->renewalTerm->getTerm();
$data['renew_term'] = $renewTermData['term'];
$data['renew_units'] = $renewTermData['unit'];
$data['renew_cost'] = $item->renewalTerm->cost->amount;
if ( $item->renewalTerm->gracePeriod )
{
$data['grace_period'] = \IPS\DateTime::create()->add( $item->renewalTerm->gracePeriod )->getTimestamp() - time();
}
}
/* Expire date? */
if ( isset( $item->expireDate ) )
{
$data['expires'] = $item->expireDate->getTimestamp();
}
/* Physical? */
if ( $item->physical )
{
$data['physical'] = TRUE;
$data['shipping'] = $item->shippingMethodIds ?: '*';
$data['weight'] = $item->weight->kilograms;
$data['length'] = $item->length->metres;
$data['width'] = $item->width->metres;
$data['height'] = $item->height->metres;
}
/* Pay to someone else? */
if ( $item->payTo )
{
$data['payTo'] = $item->payTo->member_id;
if ( $item->commission )
{
$data['commission'] = $item->commission;
}
if ( $item->fee )
{
$data['fee'] = $item->fee->amount;
}
}
/* Add it */
$items = json_decode( $this->_data['items'], TRUE );
$items[] = $data;
/* Get the ID */
$ids = array_keys( $items );
$id = array_pop( $ids );
/* Save it */
$this->items = json_encode( $items );
$this->_itemsIterator = NULL;
/* Is it a renewal */
if ( $item instanceof \IPS\nexus\Invoice\Item\Renewal )
{
$renewalIds = $this->renewal_ids;
$renewalIds[] = $item->id;
$this->renewal_ids = $renewalIds;
}
/* Increase the total */
$this->recalculateTotal();
/* Return the ID */
return $id;
}
/**
* Change item
*
* @param int $index The index
* @param array $data Data to set
* @return void
*/
public function changeItem( $index, $data )
{
$items = json_decode( $this->_data['items'], TRUE );
foreach ( $data as $k => $v )
{
$items[ $index ][ $k ] = $v;
}
$this->items = json_encode( $items );
$this->_itemsIterator = NULL;
}
/**
* Remove item
*
* @param int $index The index
* @return void
*/
public function removeItem( $index )
{
$items = json_decode( $this->_data['items'], TRUE );
unset( $items[ $index ] );
$this->items = json_encode( $items );
$this->_itemsIterator = NULL;
$this->recalculateTotal();
}
/**
* Recalculate the total amount
*
* @return void
*/
public function recalculateTotal()
{
$summary = $this->summary();
$this->total = $summary['total'];
}
/**
* Get summary for invoice tables and so on
*
* @return array
*/
public function summary()
{
$return = array();
$return['items'] = array();
$shippingItems = array();
$subtotal = 0;
$taxableAmounts = array();
$taxClasses = \IPS\nexus\Tax::roots();
/* Work out the line totals */
foreach ( $this->items as $k => $item )
{
if ( $item instanceof \IPS\nexus\extensions\nexus\Item\ShippingCharge )
{
$shippingItems[ $k ] = $item;
}
else
{
$subtotal += $item->linePrice()->amount;
if ( isset( $item->tax ) )
{
if ( isset( $taxableAmounts[ $item->tax->id ] ) )
{
$taxableAmounts[ $item->tax->id ] += $item->linePrice()->amount;
}
else
{
$taxableAmounts[ $item->tax->id ] = $item->linePrice()->amount;
}
}
$return['items'][ $k ] = $item;
}
}
/* Subtotal (subtotal is caulculated BEFORE shipping is added) */
$return['subtotal'] = new \IPS\nexus\Money( $subtotal, $this->currency );
/* Add shipping */
$return['shipping'] = array();
$shipping = 0;
foreach ( $shippingItems as $k => $item )
{
$linePrice = $item->linePrice()->amount;
$shipping += $linePrice;
if ( isset( $item->tax ) )
{
if ( isset( $taxableAmounts[ $item->tax->id ] ) )
{
$taxableAmounts[ $item->tax->id ] += $linePrice;
}
else
{
$taxableAmounts[ $item->tax->id ] = $linePrice;
}
}
$return['shipping'][ $k ] = $item;
}
$return['shippingTotal'] = new \IPS\nexus\Money( $shipping, $this->currency );
/* Now work out the tax */
$return['tax'] = array();
$tax = 0;
foreach ( $taxableAmounts as $taxId => $amount )
{
$rate = $taxClasses[ $taxId ]->rate( $this->billaddress );
$amount = $amount * $rate;
$amount = new \IPS\nexus\Money( $amount, $this->currency );
$amount = $amount->round(); // Tax value may be something like $1.005, but needs to be a valid currency amount. Always rounds up.
$return['tax'][ $taxId ] = array( 'name' => $taxClasses[ $taxId ]->_title, 'rate' => $rate, 'amount' => $amount );
$tax += $amount->amount;
}
$return['taxTotal'] = new \IPS\nexus\Money( $tax, $this->currency );
/* Add it all together */
$return['total'] = new \IPS\nexus\Money( $subtotal + $shipping + $tax, $this->currency );
$return['total'] = $return['total']->round(); // Total value must also be a valid currency amount
return $return;
}
/* !Information */
/**
* Does the invoice contain physical items that need shipping?
*
* @return bool
*/
public function hasPhysicalItems()
{
foreach ( $this->items as $item )
{
if ( $item->physical )
{
return TRUE;
}
}
return FALSE;
}
/**
* Requires login?
*
* @return bool
*/
public function requiresLogin()
{
foreach ( $this->items as $item )
{
if ( $item::$requiresAccount )
{
return TRUE;
}
}
return FALSE;
}
/**
* Amounts that go to a member
*
* @return array
*/
public function payToRecipients()
{
$recipients = array();
foreach ( $this->items as $item )
{
if ( $item->payTo )
{
if ( !isset( $recipients[ $item->payTo->member_id ] ) )
{
$recipients[ $item->payTo->member_id ] = 0;
}
$itemRecipientAmounts = $item->recipientAmounts();
$recipients[ $item->payTo->member_id ] += $itemRecipientAmounts['recipient_final']->amount;
}
}
return $recipients;
}
/**
* Get the commission information
*
* @param float $amountReceived The amount the invoice is for, minus amounts being given to other recipients
* @return array( 'rule' => \IPS\nexus\CommissionRule, 'referrer' => \IPS\nexus\Customer, 'amount' => \IPS\nexus\Money )
*/
public function commission( $amountReceived )
{
if ( !isset( $this->status_extra['commissionrule'] ) )
{
try
{
/* Get the referrer */
$referrer = \IPS\Member::load( \IPS\Db::i()->select( 'referred_by', 'nexus_referrals', array( 'member_id=?', $this->member->member_id ) )->first() );
if ( !$referrer->member_id or $referrer->inGroup( explode( ',', \IPS\Settings::i()->nexus_no_commission ) ) )
{
throw new \UnderflowException;
}
/* Get pertinent values */
$byNumber = \IPS\Db::i()->select( 'COUNT( i_id )', 'nexus_invoices', array( "i_member=? AND i_status=?", $referrer->member_id, static::STATUS_PAID ) )->first();
$byAmounts = iterator_to_array( \IPS\Db::i()->select( 'i_currency, SUM( i_total ) as value', 'nexus_invoices', array( "i_member=? AND i_status=?", $referrer->member_id, static::STATUS_PAID ), NULL, NULL, 'i_currency' )->setKeyField( 'i_currency' )->setValueField( 'value' ) );
$forNumber = \IPS\Db::i()->select( 'COUNT( i_id )', 'nexus_invoices', array( "i_member=? AND i_status=?", $this->member->member_id, static::STATUS_PAID ) )->first();
$forAmounts = iterator_to_array( \IPS\Db::i()->select( 'i_currency, SUM( i_total ) as value', 'nexus_invoices', array( "i_member=? AND i_status=?", $this->member->member_id, static::STATUS_PAID ), NULL, NULL, 'i_currency' )->setKeyField( 'i_currency' )->setValueField( 'value' ) );
/* Loop the rules to find out the best one */
$maxAmount = 0;
$actingRule = NULL;
foreach ( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_referral_rules', array( array( "rrule_by_group='*' OR " . \IPS\Db::i()->findInSet( 'rrule_by_group', $referrer->groups ) ), array( "rrule_for_group='*' OR " . \IPS\Db::i()->findInSet( 'rrule_for_group', $this->member->groups ) ) ) ), 'IPS\nexus\CommissionRule' ) as $commissionRule )
{
/* Before we go nuts, don't bother if it's not going to give us more commission */
if ( round( ( ( $amountReceived / 100 ) * $commissionRule->commission ), 2 ) <= $maxAmount )
{
continue;
}
if ( $commissionRule->commission_limit )
{
$commisionLimit = json_decode( $commissionRule->commission_limit, TRUE );
if ( isset( $commisionLimit[ $this->currency ] ) and $commisionLimit[ $this->currency ]['amount'] <= $maxAmount )
{
continue;
}
}
/* Is the invoice amount right? */
if ( $commissionRule->purchase_amount_op )
{
$amount = json_decode( $commissionRule->purchase_amount_unit, TRUE );
if ( !$this->_checkCondition( $this->total->amount, $commissionRule->purchase_amount_op, $amount[ $this->currency ] ) )
{
continue;
}
}
/* Does the referring user have the right amount of purchases? */
if ( $commissionRule->by_purchases_op )
{
$val = $commissionRule->by_purchases_unit;
if ( $commissionRule->by_purchases_type == 'v' )
{
$val = json_decode( $val, TRUE );
$val = $val[ $this->currency ];
}
if ( !$this->_checkCondition( ( $commissionRule->by_purchases_type == 'n' ? $byNumber : $byAmounts[ $this->currency ] ), $commissionRule->by_purchases_op, $val ) )
{
continue;
}
}
/* Does the purchasing user have the right amount of purchases? */
if ( $commissionRule->for_purchases_op )
{
$val = $commissionRule->for_purchases_unit;
if ( $commissionRule->for_purchases_type == 'v' )
{
$val = json_decode( $val, TRUE );
$val = $val[ $this->currency ];
}
if ( !$this->_checkCondition( ( $commissionRule->for_purchases_type == 'n' ? $forNumber : $forAmounts[ $this->currency ] ), $commissionRule->for_purchases_op, $val ) )
{
continue;
}
}
/* Work out what packages apply */
$amountToApplyCommissionOn = $amountReceived;
if ( $commissionRule->purchase_packages )
{
if ( $commissionRule->purchase_package_limit )
{
$amountToApplyCommissionOn = 0;
}
$itemsToCount = array();
foreach ( $this->items as $k => $item )
{
/* What's the package ID? */
if ( $item instanceof \IPS\nexus\extensions\nexus\Item\Package )
{
$packageId = $item->id;
}
elseif ( $commissionRule->purchase_renewal and $item instanceof \IPS\nexus\Invoice\Item\Renewal and $item->application == 'nexus' and $item->type == 'package' )
{
try
{
$packageId = \IPS\nexus\Purchase::load( $item->id )->item_id;
}
catch ( \OutOfRangeException $e ) { }
}
/* Is it in our list? */
if ( !$packageId or !in_array( $packageId, explode( ',', $commissionRule->purchase_packages ) ) )
{
continue;
}
/* Add it to the list */
if ( $packageId )
{
$itemsToCount[ $k ] = $packageId;
}
if ( $commissionRule->purchase_package_limit )
{
if ( isset( $amountReceivedForItem[ $k ] ) )
{
$amountToApplyCommissionOn += $amountReceivedForItem[ $k ];
}
else
{
$amountToApplyCommissionOn += ( $item->price->amount * $item->quantity );
}
}
}
/* If we need ALL of the packages, do we have them all? */
if ( $commissionRule->purchase_any )
{
if ( empty( $itemsToCount ) )
{
continue;
}
}
else
{
foreach ( explode( ',', $commissionRule->purchase_packages ) as $pid )
{
if ( !in_array( $pid, $itemsToCount ) )
{
continue 2;
}
}
}
}
/* If we're still here, we're applying commission */
$amountToGive = round( ( ( $amountToApplyCommissionOn / 100 ) * $commissionRule->commission ), 2 );
if ( $commissionRule->commission_limit and $commissionRule->commission_limit != '*' )
{
$limit = json_decode( $commissionRule->commission_limit, TRUE );
if ( isset( $limit[ $this->currency ] ) and $amountToGive > $limit[ $this->currency ]['amount'] )
{
$amountToGive = $limit[ $this->currency ]['amount'];
}
}
/* Is this more than what we have? */
if ( ( $amountToGive > $maxAmount ) or ( $amountToGive and !$maxAmount ) )
{
$maxAmount = $amountToGive;
$actingRule = $commissionRule;
}
}
/* Return */
$extra = $this->status_extra;
$extra['commissionrule'] = $actingRule ? $actingRule->id : 0;
$extra['commissionref'] = $referrer->member_id;
$extra['commissionamount'] = $maxAmount;
$this->status_extra = $extra;
}
catch ( \UnderflowException $e )
{
$extra = $this->status_extra;
$extra['commissionrule'] = 0;
$extra['commissionref'] = 0;
$extra['commissionamount'] = 0;
$this->status_extra = $extra;
}
$this->save();
}
return array(
'rule' => $this->status_extra['commissionrule'] ? \IPS\nexus\CommissionRule::load( $this->status_extra['commissionrule'] ) : NULL,
'referrer' => $this->status_extra['commissionref'] ? \IPS\nexus\Customer::load( $this->status_extra['commissionref'] ) : NULL,
'amount' => $this->status_extra['commissionamount'] ? new \IPS\nexus\Money( $this->status_extra['commissionamount'], $this->currency ) : NULL
);
}
/**
* Check condition
*
* @param float $a First parameter
* @param string $operator Operator (g = greater than, e = equal to, l = less than)
* @param float $b Second parameter
* @return bool
*/
protected function _checkCondition( $a, $operator, $b )
{
$a = floatval( $a );
$b = floatval( $b );
switch ( $operator )
{
case 'g':
return $a > $b;
case 'e':
return $a == $b;
case 'l':
return $a < $b;
}
return FALSE;
}
/**
* Get amount left to pay
*
* @return \IPS\nexus\Money
*/
public function amountToPay()
{
$amountToPay = $this->total;
foreach ( $this->transactions( array( \IPS\nexus\Transaction::STATUS_PAID, \IPS\nexus\Transaction::STATUS_PART_REFUNDED ) ) as $transaction )
{
if ( $transaction->currency === $this->currency )
{
$amountToPay->amount -= ( $transaction->amount->amount - ( $transaction->status === \IPS\nexus\Transaction::STATUS_PART_REFUNDED ? $transaction->partial_refund->amount : 0 ) );
}
}
return $amountToPay->round();
}
/**
* ACP Buttons
*
* @param string $ref Referer
* @return array
*/
public function buttons( $ref='v' )
{
$url = $this->acpUrl()->setQueryString( 'r', $ref );
$return = array();
if ( $this->status === \IPS\nexus\Invoice::STATUS_PENDING )
{
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'invoices_edit' ) )
{
$return['paid'] = array(
'title' => 'invoice_mark_paid',
'icon' => 'check',
'link' => $url->setQueryString( 'do', 'paid' ),
'data' => count( $this->transactions( array( \IPS\nexus\Transaction::STATUS_PENDING, \IPS\nexus\Transaction::STATUS_WAITING, \IPS\nexus\Transaction::STATUS_HELD, \IPS\nexus\Transaction::STATUS_REVIEW ) ) ) ? array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('invoice_mark_paid') ) : array( 'confirm' => '' )
);
}
if ( count( \IPS\nexus\Gateway::manualChargeGateways() ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'chargetocard' ) )
{
$return['card'] = array(
'title' => 'invoice_charge_to_card',
'icon' => 'credit-card',
'link' => $url->setQueryString( array( 'do' => 'card', '_new' => 1 ) ),
);
}
if ( isset( $this->member->cm_credits[ $this->currency ] ) and $this->member->cm_credits[ $this->currency ]->amount and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'invoices_edit' ) )
{
$return['credit'] = array(
'title' => 'invoice_charge_to_credit',
'icon' => 'money',
'link' => $url->setQueryString( 'do', 'credit' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('invoice_charge_to_credit') )
);
}
}
if ( $this->status !== \IPS\nexus\Invoice::STATUS_PAID and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'invoices_resend' ) )
{
$return['resend'] = array(
'title' => 'invoice_reissue',
'icon' => 'refresh',
'link' => $url->setQueryString( 'do', 'resend' ),
'data' => array(
'confirm' => '',
'confirmSubMessage' => \IPS\Member::loggedIn()->language()->addToStack('invoice_reissue_confirm'),
'confirmType' => 'verify',
'confirmIcon' => 'question',
'confirmButtons' => json_encode( array(
'yes' => \IPS\Member::loggedIn()->language()->addToStack('invoice_reissue_yes'),
'no' => \IPS\Member::loggedIn()->language()->addToStack('invoice_reissue_no'),
'cancel' => \IPS\Member::loggedIn()->language()->addToStack('cancel'),
) )
)
);
}
$return['print'] = array(
'title' => 'print',
'icon' => 'print',
'link' => $url->setQueryString( 'do', 'printout' ),
);
if ( $this->status !== \IPS\nexus\Invoice::STATUS_CANCELED and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'invoices_edit' ) )
{
$return['unpaid'] = array(
'title' => $this->status === \IPS\nexus\Invoice::STATUS_PAID ? 'invoice_mark_unpaid' : 'cancel',
'icon' => 'times',
'link' => $url->setQueryString( 'do', 'unpaid' ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('cancel') )
);
}
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'invoices_delete' ) )
{
$return['delete'] = array(
'title' => 'delete',
'icon' => 'times-circle',
'link' => $url->setQueryString( 'do', 'delete' ),
'data' => array( 'confirm' => '', 'confirmSubMessage' => \IPS\Member::loggedIn()->language()->addToStack('invoice_delete_warning') )
);
}
return $return;
}
/**
* Can payment be split? Returns the minnimum split amount
*
* @return FALSE|float
*/
public function canSplitPayment()
{
if ( \IPS\Settings::i()->nexus_split_payments == -1 )
{
return FALSE;
}
if ( \IPS\Settings::i()->nexus_split_payments )
{
$decoded = json_decode( \IPS\Settings::i()->nexus_split_payments, TRUE );
if ( isset( $decoded[ $this->currency ] ) )
{
if ( $decoded[ $this->currency ]['amount'] <= $this->amountToPay()->amount )
{
return $decoded[ $this->currency ]['amount'];
}
}
return FALSE;
}
else
{
return 0.01;
}
}
/* !Related Resources */
/**
* Get transactions
*
* @param array|null $statuses Statuses to get, or NULL for all
* @return \IPS\Patterns\ActiveRecordIteator
*/
public function transactions( $statuses=NULL )
{
$where = array();
$where[] = array( 't_invoice=?', $this->id );
if ( $statuses )
{
$where[] = \IPS\Db::i()->in( 't_status', $statuses );
}
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_transactions', $where ), 'IPS\nexus\Transaction' );
}
/**
* Get shipments
*
* @param array|null $statuses Statuses to get, or NULL for all
* @return \IPS\Patterns\ActiveRecordIteator
*/
public function shipments( $statuses=NULL )
{
$where = array();
$where[] = array( 'o_invoice=?', $this->id );
if ( $statuses )
{
$where[] = \IPS\Db::i()->in( 'o_status', $statuses );
}
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_ship_orders', $where ), 'IPS\nexus\Shipping\Order' );
}
/**
* Get purchases created by this invoice
*
* @return \IPS\Patterns\ActiveRecordIteator
*/
public function purchasesCreated()
{
return new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'nexus_purchases', array( 'ps_original_invoice=? AND ps_show=1', $this->id ) ), 'IPS\nexus\Purchase' );
}
/* !Actions */
/**
* Mark as paid
*
* @param \IPS\Member|NULL $member The member changing the status. Only set if the status is being manually changed by an admin.
* @return void
* @note It is the responsibility of the code calling this to log the action on the customer's account *before* calling this method
*/
public function markPaid( \IPS\Member $member = NULL )
{
if ( $this->status !== static::STATUS_PAID )
{
/* Create the member account if this was a guest */
if ( !$this->member->member_id and $this->guest_data )
{
$profileFields = $this->guest_data['profileFields'];
$memberToSave = new \IPS\nexus\Customer;
foreach ( $this->guest_data['member'] as $k => $v )
{
$memberToSave->_data[ $k ] = $v;
$memberToSave->changed[ $k ] = $v;
}
$memberToSave->save();
$this->member = $memberToSave;
$this->guest_data = NULL;
$this->save();
/* If we've entered an address during checkout, save it */
if ( $this->billaddress !== NULL )
{
$billing = new \IPS\nexus\Customer\Address;
$billing->member = $this->member;
$billing->address = $this->billaddress;
$billing->primary_billing = 1;
$billing->save();
}
if ( $this->shipaddress !== NULL )
{
$shipping = new \IPS\nexus\Customer\Address;
$shipping->member = $this->member;
$shipping->address = $this->shipaddress;
$shipping->primary_shipping = 1;
$shipping->save();
}
$profileFields['member_id'] = $memberToSave->member_id;
\IPS\Db::i()->replace( 'core_pfields_content', $profileFields );
/* Notify the incoming mail address */
if( \IPS\Settings::i()->new_reg_notify )
{
\IPS\Email::buildFromTemplate( 'core', 'registration_notify', array( $memberToSave, $profileFields ) )->send( \IPS\Settings::i()->email_in );
}
/* Update associated transactions */
\IPS\Db::i()->update( 'nexus_transactions', array( 't_member' => $this->member->member_id ), array( 't_invoice=? AND t_member=0', $this->id ) );
/* Did we use any coupons? */
foreach ( $this->items as $item )
{
if ( $item instanceof \IPS\nexus\extensions\nexus\Item\CouponDiscount )
{
$coupon = \IPS\nexus\Coupon::load( $item->id );
$uses = $coupon->used_by ? json_decode( $coupon->used_by, TRUE ) : array();
if ( isset( $uses[ $this->member->email ] ) )
{
$uses[ $this->member->member_id ] = $uses[ $this->member->email ];
unset( $uses[ $this->member->email ] );
}
else
{
$uses[ $this->member->member_id ] = 1;
}
$coupon->used_by = json_encode( $uses );
$coupon->save();
}
}
}
/* Init */
$extra = $this->status_extra;
$shipOrders = array();
/* Loop items */
$purchases = array();
$setParentIds = array();
$groupParentIds = array();
$amountReceived = $this->total->amount;
foreach ( $this->items as $k => $item )
{
/* onPaid */
$item->onPaid( $this );
/* Create a purchase record */
$purchase = NULL;
if( $item instanceof \IPS\nexus\Invoice\Item\Purchase )
{
$showPurchaseRecord = $item->showPurchaseRecord();
for ( $i = 0; $i < $item->quantity; $i++ )
{
/* Create a purchase record */
$purchase = new \IPS\nexus\Purchase;
$purchase->member = $this->member;
$purchase->name = $item->name;
if ( $item->renewalTerm )
{
$purchase->renewals = $item->renewalTerm;
$purchase->expire = \IPS\DateTime::create()->add( $item->renewalTerm->interval );
if ( $item->renewalTerm->gracePeriod )
{
$purchase->grace_period = \IPS\DateTime::create()->add( $item->renewalTerm->gracePeriod )->getTimestamp() - time();
}
}
if ( $item->expireDate )
{
$purchase->expire = $item->expireDate;
}
$purchase->app = $item::$application;
$purchase->type = $item::$type;
$purchase->item_id = intval( $item->id );
$purchase->custom_fields = $item->details;
$purchase->original_invoice = $this;
if ( $item->payTo )
{
$purchase->pay_to = $item->payTo;
$purchase->commission = $item->commission;
}
if ( $item->parent !== NULL )
{
if ( $item->parent instanceof \IPS\nexus\Purchase )
{
$purchase->parent = $item->parent;
}
else
{
$setParentIds[ $k ] = $item->parent;
if ( $item->groupWithParent )
{
$groupParentIds[ $k ] = TRUE;
}
}
}
$purchase->extra = $item->extra;
$purchase->show = (int) $showPurchaseRecord;
$purchase->save();
if( $purchase->parent and $item->groupWithParent )
{
$purchase->groupWithParent();
}
/* Make a note of the ID */
if ( !isset( $purchases[ $k ] ) )
{
$purchases[ $k ] = array();
}
$purchases[ $k ][] = $purchase;
/* Log */
if ( $showPurchaseRecord )
{
$this->member->log( 'purchase', array(
'type' => 'new',
'id' => $purchase->id,
'name' => $purchase->name,
'invoice_id' => $this->id,
'invoice_title' => $this->title
), FALSE );
}
}
}
/* Does it need shipping? */
if ( $item->physical )
{
if ( !isset( $shipOrders[ $item->chosenShippingMethodId ] ) )
{
$shipOrders[ $item->chosenShippingMethodId ] = array();
}
$shipOrders[ $item->chosenShippingMethodId ][ $k ] = $item;
}
/* Is the money going to another member? */
if ( $item->payTo and !$item->payTo->inGroup( explode( ',', \IPS\Settings::i()->nexus_no_commission ) ) )
{
$recipientAmounts = $item->recipientAmounts();
$credits = $item->payTo->cm_credits;
$credits[ $this->currency ]->amount += $recipientAmounts['recipient_final']->amount;
$item->payTo->cm_credits = $credits;
$item->payTo->save();
$amountReceived -= $recipientAmounts['recipient_final']->amount;
if ( !isset( $extra['commission'][ $item->payTo->member_id ] ) )
{
$extra['commission'][ $item->payTo->member_id ] = array( 'amount' => 0 );
}
$extra['commission'][ $item->payTo->member_id ]['amount'] += $recipientAmounts['recipient_final']->amount;
$item->payTo->log( 'comission', array(
'type' => 'purchase',
'currency' => $this->currency,
'amount' => $recipientAmounts['recipient_final']->amount,
'new_amount' => $credits[ $this->currency ]->amount,
'member' => $this->member->member_id,
'id' => $purchase ? $purchase->id : 0,
'name' => $item->name
), FALSE );
\IPS\Email::buildFromTemplate( 'nexus', 'commissionEarned', array( $this, $item, $recipientAmounts['recipient_final'] ) )
->send(
$item->payTo,
array_map(
function( $contact )
{
return $contact->alt_id->email;
},
iterator_to_array( $item->payTo->alternativeContacts( array( 'billing=1' ) ) )
),
( ( in_array( 'commission_earned', explode( ',', \IPS\Settings::i()->nexus_notify_copy_types ) ) AND \IPS\Settings::i()->nexus_notify_copy_email ) ? explode( ',', \IPS\Settings::i()->nexus_notify_copy_email ) : array() )
);
}
}
/* Referral Commission */
if ( \IPS\Settings::i()->cm_ref_on and $commission = $this->commission( $amountReceived ) and $commission['amount'] )
{
$credits = $commission['referrer']->cm_credits;
$credits[ $this->currency ]->amount += $commission['amount']->amount;
$commission['referrer']->cm_credits = $credits;
$commission['referrer']->save();
if ( !isset( $extra['commission'][ $commission['referrer']->member_id ] ) )
{
$extra['commission'][ $commission['referrer']->member_id ] = array( 'amount' => 0 );
}
$extra['commission'][ $commission['referrer']->member_id ]['amount'] += $commission['amount']->amount;
$commission['referrer']->log( 'comission', array(
'type' => 'invoice',
'currency' => $this->currency,
'amount' => $commission['amount']->amount,
'new_amount' => $credits[ $this->currency ]->amount,
'member' => $this->member->member_id,
'invoice_id' => $this->id,
'invoice_title' => $this->title
), FALSE );
$record = \IPS\Db::i()->select( '*', 'nexus_referrals', array( 'member_id=? AND referred_by=?', $this->member->member_id, $commission['referrer']->member_id ) )->first();
$amounts = $record['amount'] ? json_decode( $record['amount'], TRUE ) : array();
if ( !isset( $amounts[ $this->currency ] ) )
{
$amounts[ $this->currency ] = 0;
}
$amounts[ $this->currency ] += $commission['amount']->amount;
\IPS\Db::i()->update( 'nexus_referrals', array( 'amount' => json_encode( $amounts ) ), array( 'member_id=? AND referred_by=?', $this->member->member_id, $commission['referrer']->member_id ) );
\IPS\Email::buildFromTemplate( 'nexus', 'commissionEarned', array( $this, NULL, $commission['amount'] ) )
->send(
$commission['referrer'],
array_map(
function( $contact )
{
return $contact->alt_id->email;
},
iterator_to_array( $commission['referrer']->alternativeContacts( array( 'billing=1' ) ) )
),
( ( in_array( 'commission_earned', explode( ',', \IPS\Settings::i()->nexus_notify_copy_types ) ) AND \IPS\Settings::i()->nexus_notify_copy_email ) ? explode( ',', \IPS\Settings::i()->nexus_notify_copy_email ) : array() )
);
}
/* Set parent IDs */
foreach ( $setParentIds as $k => $v )
{
foreach ( $purchases[ $k ] as $k2 => $_purchase )
{
$_purchase->parent = isset( $purchases[ $v ][ $k2 ] ) ? $purchases[ $v ][ $k2 ] : $purchases[ $v ][0];
$_purchase->save();
if( isset( $groupParentIds[ $k ] ) )
{
$_purchase->groupWithParent();
}
}
}
/* Run callbacks */
foreach ( $purchases as $_purchases )
{
foreach ( $_purchases as $purchase )
{
$purchase->onPurchaseGenerated( $this );
}
}
/* Create shipping orders */
foreach ( $shipOrders as $method => $items )
{
$shipOrder = new \IPS\nexus\Shipping\Order;
$shipOrder->invoice = $this;
$shipOrder->data = array( 'cm_first_name' => $this->member->cm_first_name, 'cm_last_name' => $this->member->cm_last_name, 'address' => $this->shipaddress, 'cm_phone' => $this->member->cm_phone );
if ( $method )
{
try
{
$shipOrder->method = \IPS\nexus\Shipping\FlatRate::load( $method );
}
catch ( \OutOfRangeException $e )
{
$shipOrder->api_service = $method;
}
}
$shipOrder->items = $items;
$shipOrder->save();
$this->member->log( 'shipping', array(
'type' => 'new',
'id' => $shipOrder->id,
'invoice_id' => $this->id,
'invoice_title' => $this->title
), FALSE );
}
/* Refuse any pending transactions */
foreach ( $this->transactions( array( \IPS\nexus\Transaction::STATUS_PENDING, \IPS\nexus\Transaction::STATUS_WAITING, \IPS\nexus\Transaction::STATUS_HELD, \IPS\nexus\Transaction::STATUS_REVIEW ) ) as $transaction )
{
if ( $transaction->status === \IPS\nexus\Transaction::STATUS_PENDING )
{
$transaction->delete();
}
else
{
try
{
$transaction->method->void( $transaction );
}
catch ( \Exception $e ){}
$extra = $transaction->extra;
$extra['history'][] = array( 's' => \IPS\nexus\Transaction::STATUS_REFUSED, 'on' => time(), 'note' => 'invoicePaid' );
$transaction->extra = $extra;
$transaction->status = \IPS\nexus\Transaction::STATUS_REFUSED;
$transaction->save();
}
}
/* Update invoice */
$this->status = static::STATUS_PAID;
$this->paid = new \IPS\DateTime;
/* Log */
if ( $member )
{
$extra['type'] = 'manual';
$extra['setTo'] = static::STATUS_PAID;
$extra['setByID'] = $member->member_id;
$extra['date'] = time();
}
/* Save */
$this->status_extra = $extra;
$this->save();
}
}
/**
* Mark as unpaid
*
* @param string|NULL $status The status to set to
* @param \IPS\Member|NULL $member The member changing the status. Only set if the status is being manually changed by an admin.
* @return void
*/
public function markUnpaid( $status, \IPS\Member $member = NULL )
{
/* Do stuff */
if ( $this->status === static::STATUS_PAID )
{
/* Loop items */
foreach ( $this->items as $k => $item )
{
$item->onUnpaid( $this, $this->status );
/* Is the money going to another member? */
if ( $item->payTo and !$item->payTo->inGroup( explode( ',', \IPS\Settings::i()->nexus_no_commission ) ) )
{
$recipientAmounts = $item->recipientAmounts();
$credits = $item->payTo->cm_credits;
$credits[ $this->currency ]->amount -= $recipientAmounts['recipient_final']->amount;
$item->payTo->cm_credits = $credits;
$item->payTo->save();
$item->payTo->log( 'comission', array(
'type' => 'invoice_refund',
'currency' => $this->currency,
'amount' => $recipientAmounts['recipient_final']->amount,
'new_amount' => $credits[ $this->currency ]->amount,
'member' => $this->member->member_id,
'invoice_id' => $this->id,
'invoice_title' => $this->title
), FALSE );
\IPS\Email::buildFromTemplate( 'nexus', 'commissionRevoked', array( $this, $item, $recipientAmounts['recipient_final'] ) )
->send(
$item->payTo,
array_map(
function( $contact )
{
return $contact->alt_id->email;
},
iterator_to_array( $item->payTo->alternativeContacts( array( 'billing=1' ) ) )
),
( ( in_array( 'commission_earned', explode( ',', \IPS\Settings::i()->nexus_notify_copy_types ) ) AND \IPS\Settings::i()->nexus_notify_copy_email ) ? explode( ',', \IPS\Settings::i()->nexus_notify_copy_email ) : array() )
);
}
}
/* Delete purchases */
foreach ( $this->purchasesCreated() as $purchase )
{
$purchase->delete();
}
/* Delete shipping orders */
foreach ( $this->shipments() as $shipment )
{
$shipment->delete();
}
}
else
{
foreach ( $this->items as $k => $item )
{
$item->onInvoiceCancel( $this );
}
}
/* Set the status */
$this->status = $status;
/* Log */
if ( $member )
{
$extra = $this->status_extra;
$extra['type'] = 'manual';
$extra['setTo'] = $status;
$extra['setByID'] = $member->member_id;
$extra['date'] = time();
$this->status_extra = $extra;
}
/* Save */
$this->save();
}
/**
* What will happen if we mark this unpaid?
*
* @return array
*/
public function unpaidConsequences()
{
$return = array();
$purchases = $this->purchasesCreated();
if ( count( $purchases ) )
{
$return['purchases'] = array();
foreach ( $purchases as $purchase )
{
$return['purchases'][] = \IPS\Theme::i()->getTemplate('purchases')->link( $purchase );
}
}
$shipments = $this->shipments();
if ( count( $shipments ) )
{
$return['shipments'] = array();
foreach ( $shipments as $shipment )
{
if ( $shipment->status === \IPS\nexus\Shipping\Order::STATUS_SHIPPED )
{
$return['shipments'][] = \IPS\Theme::i()->getTemplate('shiporders')->link( $shipment );
}
else
{
$return['shipments'][] = array(
'message' => \IPS\Theme::i()->getTemplate('shiporders')->link( $shipment ),
'warning' => \IPS\Member::loggedIn()->language()->addToStack('invoice_unpaid_shipments_shipped'),
);
}
}
}
foreach ( $this->items as $item )
{
if ( $item->payTo )
{
$amount = $item->amountForRecipient();
$message = \IPS\Member::loggedIn()->language()->addToStack('account_credit_remove', FALSE, array( 'sprintf' => array( $amount, $item->payTo->cm_name ) ) );
$credits = $item->payTo->cm_credits;
if ( ( $credits[ $amount->currency ]->amount - $amount->amount ) < 0 )
{
$return[] = array( 'message' => $message, 'warning' => \IPS\Member::loggedIn()->language()->addToStack('account_credit_remove_neg') );
}
else
{
$return[] = $message;
}
}
if( count( $item->onUnpaidDescription( $this ) ) )
{
foreach( $item->onUnpaidDescription( $this ) as $desc )
{
$return[] = $desc;
}
}
}
return $return;
}
/**
* Send the notification to the member that this invoice has been generated
*
* @return void
*/
public function sendNotification()
{
\IPS\Email::buildFromTemplate( 'nexus', 'newInvoice', array( $this, $this->summary() ) )
->send(
$this->member,
array_map(
function( $contact )
{
return $contact->alt_id->email;
},
iterator_to_array( $this->member->alternativeContacts( array( 'billing=1' ) ) )
),
( ( in_array( 'new_invoice', explode( ',', \IPS\Settings::i()->nexus_notify_copy_types ) ) AND \IPS\Settings::i()->nexus_notify_copy_email ) ? explode( ',', \IPS\Settings::i()->nexus_notify_copy_email ) : array() )
);
}
/* !URLS */
/**
* Get checkout URL
*
* @return void
*/
public function checkoutUrl()
{
if ( !$this->id )
{
$this->save();
}
return \IPS\Http\Url::internal( 'app=nexus&module=checkout&controller=checkout&id=' . $this->id, 'front', 'nexus_checkout', array(), \IPS\Settings::i()->nexus_https );
}
/**
* @brief Cached URL
*/
protected $_url = NULL;
/**
* Get URL
*
* @return \IPS\Http\Url
*/
public function url()
{
if( $this->_url === NULL )
{
$this->_url = \IPS\Http\Url::internal( "app=nexus&module=clients&controller=invoices&do=view&id={$this->id}", 'front', 'clientsinvoice' );
}
return $this->_url;
}
/**
* ACP "View Invoice" URL
*
* @return \IPS\Http\Url
*/
public function acpUrl()
{
return \IPS\Http\Url::internal( "app=nexus&module=payments&controller=invoices&do=view&id={$this->id}", 'admin' );
}
}