Seditio Source
Root |
./othercms/ips_4.3.4/applications/nexus/modules/admin/payments/invoices.php
<?php
/**
 * @brief        Invoices
 * @author        <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
 * @copyright    (c) Invision Power Services, Inc.
 * @license        https://www.invisioncommunity.com/legal/standards/
 * @package        Invision Community
 * @subpackage    Nexus
 * @since        11 Feb 2014
 */

namespace IPS\nexus\modules\admin\payments;

/* 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;
}

/**
 * Invoices
 */
class _invoices extends \IPS\Dispatcher\Controller
{    
   
/**
     * Execute
     *
     * @return    void
     */
   
public function execute()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_manage' );
        \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'invoice.css', 'nexus', 'admin' ) );
       
parent::execute();
    }
   
   
/**
     * Manage
     *
     * @return    void
     */
   
protected function manage()
    {        
       
/* Table */
       
$url = \IPS\Http\Url::internal( 'app=nexus&module=payments&controller=invoices' );
       
$where = array();
       
$customer = NULL;
        if ( isset( \
IPS\Request::i()->member ) )
        {
            try
            {
               
$customer = \IPS\nexus\Customer::load( \IPS\Request::i()->member );
               
$url = $url->setQueryString( 'member', $customer->member_id );
               
$where[] = array( 'i_member=?', $customer->member_id );
            }
            catch ( \
OutOfRangeException $e ) { }
        }
       
$table = \IPS\nexus\Invoice::table( $where, $url, 't' );
       
$table->advancedSearch = array(
           
'i_id'        => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
           
'i_title'    => \IPS\Helpers\Table\SEARCH_CONTAINS_TEXT,
           
'i_status'    => array( \IPS\Helpers\Table\SEARCH_SELECT, array( 'options' => \IPS\nexus\Invoice::statuses(), 'multiple' => TRUE ) ),
           
'i_member'    => \IPS\Helpers\Table\SEARCH_MEMBER,
           
'i_total'    => \IPS\Helpers\Table\SEARCH_NUMERIC,
           
'i_date'    => \IPS\Helpers\Table\SEARCH_DATE_RANGE,
        );
       
$table->quickSearch = 'i_id';
       
$table->mainColumn = 'i_title';
        if (
$customer )
        {
            unset(
$table->advancedSearch['i_member'] );
        }
               
       
/* Action Buttons */
       
if( \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'invoices_add' ) )
        {
           
$generateUrl = \IPS\Http\Url::internal( "app=nexus&module=payments&controller=invoices&do=generate&_new=1" );
           
            if (
$customer )
            {
               
$generateUrl = $generateUrl->setQueryString( 'member', $customer->member_id );
            }
           
            \
IPS\Output::i()->sidebar['actions'][] = array(
               
'icon'    => 'plus',
               
'title'    => 'generate_invoice',
               
'link'    => $generateUrl
           
);
        }
        if( \
IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'invoices_settings' ) and !$customer )
        {
            \
IPS\Output::i()->sidebar['actions'][] = array(
               
'icon'    => 'cog',
               
'title'    => 'invoice_settings',
               
'link'    => \IPS\Http\Url::internal( "app=nexus&module=payments&controller=invoices&do=settings" )
            );
        }
       
       
/* Display */
       
\IPS\Output::i()->title = $customer ? \IPS\Member::loggedIn()->language()->addToStack( 'members_invoices', FALSE, array( 'sprintf' => array( $customer->cm_name ) ) ) : \IPS\Member::loggedIn()->language()->addToStack('menu__nexus_payments_invoices');
        \
IPS\Output::i()->output = (string) $table;
    }
   
   
/**
     * View
     *
     * @return    void
     */
   
public function view()
    {
       
/* Load Invoice */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/3', 404, '' );
        }
               
       
/* Get transactions */
       
$transactions = NULL;
        if( \
IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_manage' ) and ( !isset( \IPS\Request::i()->table ) ) )
        {
           
$transactions = \IPS\nexus\Transaction::table( array( array( 't_invoice=? and t_status<>?', $invoice->id, \IPS\nexus\Transaction::STATUS_PENDING ) ), $invoice->acpUrl(), 'i' );
           
$transactions->limit = 50;
           
$transactions->tableTemplate = array( \IPS\Theme::i()->getTemplate('invoices'), 'transactionsTable' );
           
$transactions->rowsTemplate = array( \IPS\Theme::i()->getTemplate('invoices'), 'transactionsTableRows' );

            foreach (
$transactions->include as $k => $v )
            {
                if (
in_array( $v, array( 't_member', 't_invoice' ) ) )
                {
                    unset(
$transactions->include[ $k ] );
                }
            }
        }
       
       
/* Get shipments */
       
$shipments = NULL;
        if( \
IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'shiporders_manage' ) )
        {
            if ( \
IPS\Db::i()->select( 'COUNT(*)', 'nexus_ship_orders', array( 'o_invoice=?', $invoice->id ) )->first() )
            {
               
$shipments = new \IPS\Helpers\Table\Db( 'nexus_ship_orders', $invoice->acpUrl()->setQueryString( 'table', 'shipments' ), array( 'o_invoice=?', $invoice->id ) );
               
$shipments->sortBy = $shipments->sortBy ?: 'o_shipped_date';
               
$shipments->include = array( 'o_status', 'o_method', 'o_shipped_date' );
               
$shipments->limit = 50;
               
$shipments->tableTemplate = array( \IPS\Theme::i()->getTemplate('invoices'), 'shipmentsTable' );
               
$shipments->rowsTemplate = array( \IPS\Theme::i()->getTemplate('invoices'), 'shipmentsTableRows' );

               
$shipments->parsers = array(
                   
'o_status'    => function( $val )
                    {                        
                        return \
IPS\Theme::i()->getTemplate('shiporders')->status( $val );
                    },
                   
'o_method'    => function( $val, $row )
                    {
                        if (
$row['o_api_service'] )
                        {
                            return
$row['o_api_service'];
                        }
                       
                        try
                        {
                            return \
IPS\nexus\Shipping\FlatRate::load( $val )->_title;
                        }
                        catch ( \
Exception $e )
                        {
                            return
'';
                        }
                    },
                   
'o_shipped_date'    => function( $val )
                    {
                        return
$val ? ( (string) \IPS\DateTime::ts( $val ) ) : \IPS\Member::loggedIn()->language()->addToStack('not_shipped_yet');
                    }
                );
               
$shipments->rowButtons = function( $row )
                {
                    return
array_merge( array(
                       
'view'    => array(
                           
'icon'    => 'search',
                           
'title'    => 'shipment_view',
                           
'link'    => \IPS\Http\Url::internal( "app=nexus&module=payments&controller=shipping&do=view&id={$row['o_id']}" )
                        ),
                    ), \
IPS\nexus\Shipping\Order::constructFromData( $row )->buttons( 'i' ) );
                };
            }
        }

       
/* Add Buttons */
       
\IPS\Output::i()->sidebar['actions'] = $invoice->buttons( 'v' );
       
       
/* Output */
       
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'invoice_number', FALSE, array( 'sprintf' => array( $invoice->id ) ) );
        \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate( 'invoices' )->view( $invoice, $invoice->summary(), $transactions, (string) $shipments );
    }
   
   
/**
     * Paid
     *
     * @return    void
     */
   
public function paid()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_edit' );
       
       
/* Load Invoice */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/6', 404, '' );
        }
       
       
/* Any pending transactions? */
       
if ( !isset( \IPS\Request::i()->override ) and \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_manage' ) )
        {
           
$pendingTransactions = $invoice->transactions( array( \IPS\nexus\Transaction::STATUS_WAITING, \IPS\nexus\Transaction::STATUS_HELD, \IPS\nexus\Transaction::STATUS_REVIEW, \IPS\nexus\Transaction::STATUS_GATEWAY_PENDING ) );
            if (
count( $pendingTransactions ) )
            {
               
$transUrl = $invoice->acpUrl();
                if (
count( $pendingTransactions ) === 1 )
                {
                    foreach (
$pendingTransactions as $transaction )
                    {
                       
$transUrl = $transaction->acpUrl();
                    }
                }
               
                \
IPS\Output::i()->output = \IPS\Theme::i()->getTemplate('global', 'core')->decision( 'invoice_paid_trans', array(
                   
'invoice_paid_trans_view'    => $transUrl,
                   
'invoice_paid_trans_ovrd'    => $invoice->acpUrl()->setQueryString( array( 'do' => 'paid', 'override' => 1 ) )
                ) );
                return;
            }
        }
       
       
/* Log (do this first so the log appears in the correct order) */
       
$invoice->member->log( 'invoice', array(
           
'type'    => 'status',
           
'new'    => \IPS\nexus\Invoice::STATUS_PAID,
           
'id'    => $invoice->id,
           
'title' => $invoice->title
       
) );
       
       
/* Send Email */
       
\IPS\Email::buildFromTemplate( 'nexus', 'invoiceMarkedPaid', array( $invoice, $invoice->summary() ), \IPS\Email::TYPE_TRANSACTIONAL )
            ->
send(
               
$invoice->member,
               
array_map(
                    function(
$contact )
                    {
                        return
$contact->alt_id->email;
                    },
                   
iterator_to_array( $invoice->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() )
            );
       
       
/* Do it */
       
$invoice->markPaid( \IPS\Member::loggedIn() );
       
       
/* Redirect */
       
$this->_redirect( $invoice );
    }
   
   
/**
     * Charge to card
     *
     * @return    void
     */
   
public function card()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'chargetocard' );
        \
IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'customer.css', 'nexus', 'admin' ) );

       
/* Load Invoice */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/8', 404, '' );
        }
       
       
/* Get gateways */
       
$gateways = \IPS\nexus\Gateway::manualChargeGateways();
       
       
/* Can we do this? */
       
if ( $invoice->status !== \IPS\nexus\Invoice::STATUS_PENDING or !count( $gateways ) )
        {
            \
IPS\Output::i()->error( 'invoice_status_err', '2X190/9', 403, '' );
        }

       
$self = $this;
       
/* Wizard */
       
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'invoice_charge_to_card' );
        \
IPS\Output::i()->output = new \IPS\Helpers\Wizard(
            array(
               
't_amount'        => function( $data ) use ( $invoice )
                {                    
                   
$amountToPay = $invoice->amountToPay()->amount;
                   
$form = new \IPS\Helpers\Form( 'amount', 'continue' );
                   
$form->add( new \IPS\Helpers\Form\Number( 't_amount', $amountToPay, TRUE, array( 'min' => 0.01, 'max' => (string) $amountToPay, 'decimals' => TRUE ), NULL, NULL, $invoice->currency ) );
                    if (
$values = $form->values() )
                    {
                        return
$values;
                    }
                    return
$form;
                },
               
'checkout_pay'    => function( $data ) use ( $invoice, $gateways, $self )
                {
                   
$amountToPay = new \IPS\nexus\Money( $data['t_amount'], $invoice->currency );
                   
                   
/* Get elements */
                   
$elements = array();
                   
$paymentMethodsToggles = array();
                    foreach (
$gateways as $gateway )
                    {
                        foreach (
$gateway->paymentScreen( $invoice, $amountToPay, $invoice->member ) as $element )
                        {
                            if ( !
$element->htmlId )
                            {
                               
$element->htmlId = $gateway->id . '-' . $element->name;
                            }
                           
$elements[] = $element;
                           
$paymentMethodsToggles[ $gateway->id ][] = $element->htmlId;
                        }
                    }
                   
$paymentMethodOptions = array();
                    foreach (
$gateways as $k => $v )
                    {
                       
$paymentMethodOptions[ $k ] = $v->_title;
                    }
                   
                   
/* Build form */
                   
$form = new \IPS\Helpers\Form( 'charge', 'invoice_charge_to_card' );
                    if (
count( $gateways ) > 1 )
                    {
                       
$form->add( new \IPS\Helpers\Form\Radio( 'payment_method', NULL, TRUE, array( 'options' => $paymentMethodOptions, 'toggles' => $paymentMethodsToggles ) ) );
                    }
                    foreach (
$elements as $element )
                    {
                       
$form->add( $element );
                    }
                       
                   
/* Handle submissions */
                   
if ( $values = $form->values() )
                    {
                        if (
count( $gateways ) === 1 )
                        {
                           
$gateway = array_pop( $gateways );
                        }
                        else
                        {
                           
$gateway = $gateways[ $values['payment_method'] ];
                        }
                       
                       
$transaction = new \IPS\nexus\Transaction;
                       
$transaction->member = $invoice->member;
                       
$transaction->invoice = $invoice;
                       
$transaction->method = $gateway;
                       
$transaction->amount = $amountToPay;                        
                       
$transaction->currency = $invoice->currency;
                       
$transaction->ip = \IPS\Request::i()->ipAddress();
                       
$transaction->extra = array( 'admin' => \IPS\Member::loggedIn()->member_id );
                       
                        try
                        {
                           
$transaction->auth = $gateway->auth( $transaction, $values );
                           
$transaction->capture();
                           
                           
$transaction->member->log( 'transaction', array(
                               
'type'            => 'paid',
                               
'status'        => \IPS\nexus\Transaction::STATUS_PAID,
                               
'id'            => $transaction->id,
                               
'invoice_id'    => $invoice->id,
                               
'invoice_title'    => $invoice->title,
                            ) );
                           
                           
$transaction->approve();
                           
                           
$transaction->sendNotification();
                           
                           
$self->_redirect( $invoice );
                        }
                        catch ( \
Exception $e )
                        {
                           
$form->error = $e->getMessage();
                            return
$form;
                        }                        
                    }
                   
                   
/* Display form */
                   
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'global_gateways.js', 'nexus', 'global' ) );
                    return
$form;
                }

            ),
           
$invoice->acpUrl()->setQueryString( 'do', 'card' )
        );
    }
   
   
/**
     * Charge to account credit
     *
     * @return    void
     */
   
public function credit()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_edit' );
       
       
/* Load Invoice */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/A', 404, '' );
        }
               
       
/* Can we do this? */
       
if ( $invoice->status !== \IPS\nexus\Invoice::STATUS_PENDING )
        {
            \
IPS\Output::i()->error( 'invoice_status_err', '2X190/B', 403, '' );
        }
       
       
/* How much can we do? */
       
$amountToPay = $invoice->amountToPay()->amount;
       
$credits = $invoice->member->cm_credits;
       
$credit = $credits[ $invoice->currency ]->amount;
       
$maxCanCharge = ( $credit->compare( $amountToPay ) === -1 ) ? $credit : $amountToPay;

       
/* Build Form */
       
$form = new \IPS\Helpers\Form( 'amount', 'invoice_charge_to_credit' );
       
$form->add( new \IPS\Helpers\Form\Number( 't_amount', $maxCanCharge, TRUE, array( 'min' => 0.01, 'max' => (string) $maxCanCharge, 'decimals' => TRUE ), NULL, NULL, $invoice->currency ) );
       
       
/* Handle submissions */
       
if ( $values = $form->values() )
        {            
           
$transaction = new \IPS\nexus\Transaction;
           
$transaction->member = $invoice->member;
           
$transaction->invoice = $invoice;
           
$transaction->amount = new \IPS\nexus\Money( $values['t_amount'], $invoice->currency );
           
$transaction->ip = \IPS\Request::i()->ipAddress();
           
$transaction->extra = array( 'admin' => \IPS\Member::loggedIn()->member_id );
           
$transaction->save();
           
$transaction->approve( NULL );
           
$transaction->sendNotification();
           
           
$credits[ $invoice->currency ]->amount = $credits[ $invoice->currency ]->amount->subtract( $transaction->amount->amount );
           
$invoice->member->cm_credits = $credits;
           
$invoice->member->save();
           
           
$invoice->member->log( 'transaction', array(
               
'type'            => 'paid',
               
'status'        => \IPS\nexus\Transaction::STATUS_PAID,
               
'id'            => $transaction->id,
               
'invoice_id'    => $invoice->id,
               
'invoice_title'    => $invoice->title,
            ) );
           
           
$this->_redirect( $invoice );
        }
       
       
/* Display */
       
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( 'invoice_charge_to_credit' );
        \
IPS\Output::i()->output = $form;
    }
   
   
/**
     * Reissue
     *
     * @return    void
     */
   
public function resend()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_resend' );
       
       
/* Load Invoice */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/C', 404, '' );
        }
       
       
/* Update */
       
$invoice->date = new \IPS\DateTime;
       
$invoice->status = \IPS\nexus\Invoice::STATUS_PENDING;
       
$invoice->save();
       
       
/* Send email */
       
$emailSent = FALSE;
        if ( isset( \
IPS\Request::i()->prompt ) and \IPS\Request::i()->prompt )
        {
           
$emailSent = TRUE;
           
$invoice->sendNotification();
        }
       
       
/* Log */
       
$invoice->member->log( 'invoice', array( 'type' => 'resend', 'id' => $invoice->id, 'title' => $invoice->title, 'email' => $emailSent ) );
       
       
/* Redirect */
       
$this->_redirect( $invoice );
    }
   
   
/**
     * Print
     *
     * @return    void
     */
   
public function printout()
    {
       
/* Load */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/D', 404, '' );
        }

       
/* Get the address */
       
$address = NULL;
        if (
$invoice->billaddress )
        {
           
$address = $invoice->billaddress;
        }
        else
        {
            try
            {
               
$address = \IPS\nexus\Customer\Address::constructFromData( \IPS\Db::i()->select( '*', 'nexus_customer_addresses', array( "member=? AND primary_billing=?", $invoice->member->member_id, 1 ) )->first() )->address;
            }
            catch( \
UnderflowException $e ) {}
        }
       
       
/* Get output */
       
$output = \IPS\Theme::i()->getTemplate( 'invoices', 'nexus', 'global' )->printInvoice( $invoice, $invoice->summary(), $address );
        \
IPS\Output::i()->title = 'I' . $invoice->id;
        \
IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->blankTemplate( $output ) );
    }
   
   
/**
     * Unpaid
     *
     * @return    void
     */
   
public function unpaid()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_edit' );
       
       
/* Load Invoice */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/7', 404, '' );
        }
                       
       
/* Get paid transactions */
       
$transactions = $invoice->transactions( array( \IPS\nexus\Transaction::STATUS_PAID, \IPS\nexus\Transaction::STATUS_PART_REFUNDED ) );
       
       
/* Build form */
       
$form = new \IPS\Helpers\Form;
       
       
/* Ask what we want to do with the transactions */
       
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_refund' ) )
        {
            foreach (
$transactions as $transaction )
            {
               
/* What refund options are available? */
               
$method = $transaction->method;
               
$refundMethods = array();
               
$refundMethodToggles = array( 'credit' => array( $transaction->id . '_refund_amount' ) );
               
$refundReasons = array();
                if (
$method and $method::SUPPORTS_REFUNDS )
                {
                   
$refundMethods['gateway'] = $method->_title;
                   
$refundMethodToggles['gateway'] = array();
                    if (
$method::SUPPORTS_PARTIAL_REFUNDS )
                    {
                       
$refundMethodToggles['gateway'][] = $transaction->id . '_refund_amount';
                    }
                    if (
$refundReasons = $method::refundReasons() )
                    {
                       
$refundMethodToggles['gateway'][] = $transaction->id . '_refund_reason';
                    }
                }
               
$refundMethods['credit'] = 'refund_method_credit';
               
$refundMethods['none'] = 'refund_method_none';
               
               
/* How do we want to refund? */
               
$field = new \IPS\Helpers\Form\Radio( $transaction->id . '_refund_method', 'none', TRUE, array( 'options' => $refundMethods, 'toggles' => $refundMethodToggles ) );
               
$field->label = count( $transactions ) === 1 ? \IPS\Member::loggedIn()->language()->addToStack( 'refund_method' ) : \IPS\Member::loggedIn()->language()->addToStack( 'trans_refund_method', FALSE, array( 'sprintf' => array( $transaction->id ) ) );
               
$form->add( $field );
                if (
$refundReasons )
                {
                   
$field = new \IPS\Helpers\Form\Radio( $transaction->id . '_refund_reason', NULL, FALSE, array( 'options' => $refundReasons ), NULL, NULL, NULL, $transaction->id . '_refund_reason' );
                   
$field->label = count( $transactions ) === 1 ? \IPS\Member::loggedIn()->language()->addToStack( 'refund_reason' ) : \IPS\Member::loggedIn()->language()->addToStack( 'trans_refund_reason', FALSE, array( 'sprintf' => array( $transaction->id ) ) );
                   
$form->add( $field );
                }
               
               
/* Partial refund? */
               
$field = new \IPS\Helpers\Form\Number( $transaction->id . '_refund_amount', ( $transaction->status === \IPS\nexus\Transaction::STATUS_PART_REFUNDED ? (string) $transaction->amount->amount->subtract( $transaction->partial_refund->amount ) : 0 ), TRUE, array( 'unlimited' => 0, 'unlimitedLang' => \IPS\Member::loggedIn()->language()->addToStack( 'refund_full', FALSE, array( 'sprintf' => array( $transaction->amount ) ) ), 'max' => (string) $transaction->amount->amount->subtract( $transaction->partial_refund->amount ), 'decimals' => TRUE ), NULL, NULL, $transaction->amount->currency, $transaction->id . '_refund_amount' );
               
$field->label = \IPS\Member::loggedIn()->language()->addToStack( 'refund_amount' );
               
$form->add( $field );
            }
        }
       
       
/* Do we want to mark the invoice as pending or canceled? */
       
if ( $invoice->status === \IPS\nexus\Invoice::STATUS_PAID )
        {
           
$statusOptions = array();
            if ( !
$invoice->total->amount->isZero() )
            {
               
$statusOptions[ \IPS\nexus\Invoice::STATUS_PENDING ] = 'refund_invoice_pending';
            }
            if ( \
IPS\Settings::i()->cm_invoice_expireafter )
            {
               
$statusOptions[ \IPS\nexus\Invoice::STATUS_EXPIRED ] = 'refund_invoice_expired';
            }
           
$statusOptions[ \IPS\nexus\Invoice::STATUS_CANCELED ] = 'refund_invoice_canceled';
           
$field = new \IPS\Helpers\Form\Radio( 'refund_invoice_status', \IPS\nexus\Invoice::STATUS_CANCELED, TRUE, array( 'options' => $statusOptions ) );
           
$field->warningBox = \IPS\Theme::i()->getTemplate('invoices')->unpaidConsequences( $invoice );
           
$form->add( $field );
        }
        else
        {
           
$statusOptions = array();
            if ( \
IPS\Settings::i()->cm_invoice_expireafter )
            {
               
$statusOptions[ \IPS\nexus\Invoice::STATUS_EXPIRED ] = 'invoice_status_expd';
            }
           
$statusOptions[ \IPS\nexus\Invoice::STATUS_CANCELED ] = 'invoice_status_canc';
           
$form->add( new \IPS\Helpers\Form\Radio( 'refund_invoice_status', \IPS\nexus\Invoice::STATUS_CANCELED, TRUE, array( 'options' => $statusOptions ) ) );
        }

       
/* Handle submissions */
       
if ( $values = $form->values() )
        {
           
/* Refund transactions */
           
if ( \IPS\Member::loggedIn()->hasAcpRestriction( 'nexus', 'payments', 'transactions_refund' ) )
            {
                foreach (
$transactions as $transaction )
                {
                    try
                    {
                       
$transaction->refund( $values[ $transaction->id . '_refund_method' ], $values[ $transaction->id . '_refund_amount' ], isset( $values[ $transaction->id . '_refund_reason' ] ) ? $values[ $transaction->id . '_refund_reason' ] : NULL );
                    }
                    catch ( \
LogicException $e )
                    {
                        \
IPS\Output::i()->error( $e->getMessage(), '1X190/1', 500, '' );
                    }
                    catch ( \
RuntimeException $e )
                    {
                        \
IPS\Output::i()->error( 'refund_failed', '3X190/2', 500, '' );
                    }
                }
            }
           
           
/* Log */
           
$invoice->member->log( 'invoice', array(
               
'type'    => 'status',
               
'new'    => $values['refund_invoice_status'],
               
'id'    => $invoice->id,
               
'title' => $invoice->title
           
) );
           
           
/* Change invoice status */
           
$invoice->markUnpaid( $values['refund_invoice_status'], \IPS\Member::loggedIn() );
           
           
/* Boink */
           
$this->_redirect( $invoice );
        }
       
       
/* Display */
       
\IPS\Output::i()->output = $form;
    }
   
   
/**
     * PO Number
     *
     * @return    void
     */
   
public function poNumber()
    {
        try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/4', 404, '' );
        }
       
       
$form = new \IPS\Helpers\Form;
       
$form->add( new \IPS\Helpers\Form\Text( 'invoice_po_number', $invoice->po, FALSE, array( 'maxLength' => 255 ) ) );
        if (
$values = $form->values() )
        {
           
$invoice->po = $values['invoice_po_number'];
           
$invoice->save();
           
$this->_redirect( $invoice );
        }
        \
IPS\Output::i()->output = $form;
    }
   
   
/**
     * Notes
     *
     * @return    void
     */
   
public function notes()
    {
        try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/5', 404, '' );
        }
       
       
$form = new \IPS\Helpers\Form;
       
$form->add( new \IPS\Helpers\Form\TextArea( 'invoice_notes', $invoice->notes ) );
        if (
$values = $form->values() )
        {
           
$invoice->notes = $values['invoice_notes'];
           
$invoice->save();
           
$this->_redirect( $invoice );
        }
        \
IPS\Output::i()->output = $form;
    }
   
   
/**
     * Delete
     *
     * @return    void
     */
   
public function delete()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_delete' );
       
       
/* Make sure the user confirmed the deletion */
       
\IPS\Request::i()->confirmedDelete();
       
       
/* Load Transaction */
       
try
        {
           
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
        }
        catch ( \
OutOfRangeException $e )
        {
            \
IPS\Output::i()->error( 'node_error', '2X190/E', 404, '' );
        }
               
       
/* Log it */
       
$invoice->member->log( 'invoice', array(
           
'type'        => 'delete',
           
'id'        => $invoice->id,
           
'title'        => $invoice->title
       
) );
       
       
/* Delete */
       
$invoice->delete();
       
       
/* Redirect */
       
\IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=nexus&module=payments&controller=invoices') );
    }
   
   
/**
     * Generate
     *
     * @return    void
     */
   
public function generate()
    {
       
/* Init */
       
\IPS\Dispatcher::i()->checkAcpPermission( 'invoices_add' );
        \
IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'admin_store.js', 'nexus', 'admin' ) );
       
$url = \IPS\Http\Url::internal("app=nexus&module=payments&controller=invoices&do=generate");
        if ( isset( \
IPS\Request::i()->member ) )
        {
           
$url = $url->setQueryString( 'member', \IPS\Request::i()->member );
        }
       
       
/* Are we editing an invoice? */
       
$invoice = NULL;
        if ( isset( \
IPS\Request::i()->id ) )
        {
            try
            {
               
$invoice = \IPS\nexus\Invoice::load( \IPS\Request::i()->id );
               
                if (
$invoice->status === \IPS\nexus\Invoice::STATUS_PAID )
                {
                    \
IPS\Output::i()->error( 'cannot_edit_paid_invoice', '2X190/F', 403, '' );
                }
               
               
$url = $url->setQueryString( 'id', \IPS\Request::i()->id );
            }
            catch ( \
OutOfRangeException $e ) { }
        }
        \
IPS\Output::i()->title = $invoice ? \IPS\Member::loggedIn()->language()->addToStack( 'invoice_number', FALSE, array( 'sprintf' => array( $invoice->id ) ) ) : \IPS\Member::loggedIn()->language()->addToStack('generate_invoice');
           
       
/* Select Customer */    
       
$steps = array();
        if ( !isset( \
IPS\Request::i()->member ) and !$invoice )
        {
           
$steps['invoice_generate_member'] = function( $data )
            {
               
$form = new \IPS\Helpers\Form('customer', 'continue');
               
$form->add( new \IPS\Helpers\Form\Member( 'invoice_generate_member', NULL, TRUE ) );
                if (
$values = $form->values() )
                {
                    return array(
'member' => $values['invoice_generate_member']->member_id );
                }
                return
$form;
            };
        }
       
       
/* Select Addresses */
       
$steps['invoice_generate_settings'] = function( $data ) use ( $invoice )
        {
           
$customer = $invoice ? $invoice->member : ( \IPS\nexus\Customer::load( isset( \IPS\Request::i()->member ) ? \IPS\Request::i()->member : $data['member'] ) );
           
           
$form = new \IPS\Helpers\Form('settings', 'continue');
           
           
$form->addHeader( 'invoice_settings' );
           
$currencies = \IPS\nexus\Money::currencies();
            if (
count( $currencies ) > 1 )
            {
               
$form->add( new \IPS\Helpers\Form\Radio( 'currency', $invoice ? $invoice->currency : $customer->defaultCurrency(), TRUE, array( 'options' => array_combine( $currencies, $currencies ) ) ) );
            }
           
$statusOptions = array();
           
$statusOptions[ \IPS\nexus\Invoice::STATUS_PAID ] = 'invoice_status_paid';
           
$statusOptions[ \IPS\nexus\Invoice::STATUS_PENDING ] = 'invoice_status_pend';
            if ( \
IPS\Settings::i()->cm_invoice_expireafter )
            {
               
$statusOptions[ \IPS\nexus\Invoice::STATUS_EXPIRED ] = 'invoice_status_expd';
            }
           
$statusOptions[ \IPS\nexus\Invoice::STATUS_CANCELED ] = 'invoice_status_canc';
           
$form->add( new \IPS\Helpers\Form\Radio( 'invoice_status', $invoice ? $invoice->status : \IPS\nexus\Invoice::STATUS_PENDING, TRUE, array( 'options' => $statusOptions ) ) );                    
           
$form->add( new \IPS\Helpers\Form\Text( 'invoice_title', $invoice ? $invoice->title : NULL, FALSE ) );
           
$form->add( new \IPS\Helpers\Form\Text( 'invoice_po_number', $invoice ? $invoice->po_number : NULL, FALSE, array( 'maxLength' => 255 ) ) );
           
$form->add( new \IPS\Helpers\Form\TextArea( 'invoice_notes', $invoice ? $invoice->notes : NULL ) );
           
           
$form->addHeader( 'invoice_generate_addresses' );
           
$addresses = \IPS\Db::i()->select( '*', 'nexus_customer_addresses', array( 'member=?', $customer->member_id ) );
            if (
count( $addresses ) )
            {
               
$primaryBillingAddressId = NULL;
               
$chosenBillingAddressId = 0;
               
$primaryShippingAddressId = 'x';
               
$chosenShippingAddressid = 0;
               
$options = array();
                foreach ( new \
IPS\Patterns\ActiveRecordIterator( $addresses, 'IPS\nexus\Customer\Address' ) as $address )
                {
                   
$options[ $address->id ] = (string) $address->address;
                    if (
$address->primary_billing )
                    {
                       
$primaryBillingAddressId = $address->id;
                    }
                    if (
$address->primary_shipping )
                    {
                       
$primaryShippingAddressId = $address->id;
                    }
                   
                    if (
$invoice and $invoice->billaddress and $invoice->billaddress == $address->address )
                    {
                       
$chosenBillingAddressId = $address->id;
                    }
                    if (
$invoice and $invoice->shipaddress and $invoice->shipaddress == $address->address )
                    {
                       
$chosenShippingAddressid = $address->id;
                    }
                }
               
$options[0] = 'other';
               
               
$form->add( new \IPS\Helpers\Form\Radio( 'billing_address', ( $invoice and $invoice->billaddress ) ? $chosenBillingAddressId : $primaryBillingAddressId, TRUE, array( 'options' => $options, 'toggles' => array( 0 => array( 'new_billing_address' ) ) ) ) );
               
$newAddress = new \IPS\Helpers\Form\Address( 'new_billing_address', $invoice ? $invoice->billaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_billing_address' );
               
$newAddress->label = ' ';
               
$form->add( $newAddress );
               
               
$form->add( new \IPS\Helpers\Form\Radio( 'shipping_address', ( $invoice and $invoice->shipaddress ) ? $chosenShippingAddressid : ( ( $primaryShippingAddressId == $primaryBillingAddressId ) ? 'x' : $primaryShippingAddressId ), TRUE, array( 'options' => array( 'x' => 'same_as_billing_address' ) + $options, 'toggles' => array( 0 => array( 'new_shipping_address' ) ) ) ) );
               
$newAddress = new \IPS\Helpers\Form\Address( 'new_shipping_address', $invoice ? $invoice->shipaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_shipping_address' );
               
$newAddress->label = ' ';
               
$form->add( $newAddress );
            }
            else
            {
               
$form->add( new \IPS\Helpers\Form\Address( 'new_billing_address', $invoice ? $invoice->billaddress : NULL, FALSE ) );
               
$form->add( new \IPS\Helpers\Form\Radio( 'shipping_address', ( $invoice and $invoice->shipaddress and $invoice->shipaddress != $invoice->billaddress ) ? '1' : 'x', FALSE, array( 'options' => array( 'x' => 'same_as_billing_address', '1' => 'other' ), 'toggles' => array( '1' => array( 'new_shipping_address' ) ) ) ) );
               
$form->add( new \IPS\Helpers\Form\Address( 'new_shipping_address', $invoice ? $invoice->billaddress : NULL, FALSE, array(), NULL, NULL, NULL, 'new_shipping_address' ) );
            }
           
            if (
$values = $form->values() )
            {
                if (
count( $addresses ) and $values['billing_address'] )
                {
                   
$data['billaddress'] = \IPS\nexus\Customer\Address::load( $values['billing_address'] )->address;
                }
                else
                {
                    if(
$values['new_billing_address'] === NULL OR empty( $values['new_billing_address']->addressLines ) or !$values['new_billing_address']->city or !$values['new_billing_address']->country or ( !$values['new_billing_address']->region and array_key_exists( $values['new_billing_address']->country, \IPS\GeoLocation::$states ) ) or !$values['new_billing_address']->postalCode )
                    {
                       
$data['billaddress'] = NULL;
                    }
                    else
                    {
                       
$data['billaddress'] = $values['new_billing_address'];
                    }
                }

                if (
count( $addresses ) and $values['shipping_address'] and $values['shipping_address'] !== 'x' )
                {
                   
$data['shipaddress'] = \IPS\nexus\Customer\Address::load( $values['shipping_address'] )->address;
                }
                elseif (
$values['shipping_address'] === 'x' )
                {
                   
$data['shipaddress'] = $data['billaddress'];
                }
                else
                {
                    if(
$values['new_shipping_address'] === NULL OR empty( $values['new_shipping_address']->addressLines ) or !$values['new_shipping_address']->city or !$values['new_shipping_address']->country or ( !$values['new_shipping_address']->region and array_key_exists( $values['new_shipping_address']->country, \IPS\GeoLocation::$states ) ) or !$values['new_shipping_address']->postalCode )
                    {
                       
$data['shipaddress'] = NULL;
                    }
                    else
                    {
                       
$data['shipaddress'] = $values['new_shipping_address'];
                    }
                }
                               
               
$data['currency'] = isset( $values['currency'] ) ? $values['currency'] : $customer->defaultCurrency();
               
$data['status'] = $values['invoice_status'];
               
$data['title'] = $values['invoice_title'];
               
$data['po_number'] = $values['invoice_po_number'];
               
$data['notes'] = $values['invoice_notes'];
               
                if (
$invoice )
                {
                   
$data['items'] = $invoice->items;
                }
               
                return
$data;
            }
           
            return
$form;
        };
       
       
/* Add Items */
       
$steps['invoice_generate_items'] = function( $data ) use ( $url, $invoice )
        {
            if ( !
$invoice )
            {
               
$invoice = new \IPS\nexus\Invoice;
               
$invoice->member = \IPS\Member::load( isset( \IPS\Request::i()->member ) ? \IPS\Request::i()->member : $data['member'] );
            }
           
$invoice->currency = $data['currency'];
            if (
$data['billaddress'] )
            {
               
$invoice->billaddress = $data['billaddress'];
            }
            if (
$data['shipaddress'] )
            {    
               
$invoice->shipaddress = $data['shipaddress'];
            }
           
$invoice->items = isset( $data['items'] ) ? json_encode( $data['items'] ) : json_encode( array() );            
                                                                       
            if ( isset( \
IPS\Request::i()->continue ) )
            {
               
$invoice->recalculateTotal();

                if (
$data['title'] )
                {
                   
$invoice->title = $data['title'];
                }
                if (
$data['po_number'] )
                {
                   
$invoice->po = $data['po_number'];
                }
                if (
$data['notes'] )
                {
                   
$invoice->notes = $data['notes'];
                }
               
                if (
$data['status'] === \IPS\nexus\Invoice::STATUS_PAID )
                {
                   
$invoice->status = \IPS\nexus\Invoice::STATUS_PENDING;
                   
$invoice->save();
                   
                   
$invoice->member->log( 'invoice', array(
                       
'type'    => 'status',
                       
'new'    => \IPS\nexus\Invoice::STATUS_PAID,
                       
'id'    => $invoice->id,
                       
'title' => $invoice->title
                   
) );
                   
$invoice->markPaid();
                }
                else
                {
                   
$invoice->status = $data['status'];
                   
$invoice->save();
                }
               
               
$invoice->sendNotification();
               
                \
IPS\Output::i()->redirect( $invoice->acpUrl() );
            }
            elseif ( isset( \
IPS\Request::i()->remove ) )
            {
                unset(
$data['items'][ \IPS\Request::i()->remove ] );
               
$_SESSION[ 'wizard-' . md5( $url ) . '-data' ] = $data;
                \
IPS\Output::i()->redirect( $url );
            }
            elseif ( isset( \
IPS\Request::i()->addRenewal ) )
            {
               
$form = new \IPS\Helpers\Form;
               
$form->add( new \IPS\Helpers\Form\Node( 'purchases_to_renew', NULL, TRUE, array( 'class' => 'IPS\nexus\Purchase', 'forceOwner' => $invoice->member, 'multiple' => TRUE, 'permissionCheck' => function( $purchase )
                {
                    return (bool)
$purchase->renewals;
                } ) ) );
               
$form->add( new \IPS\Helpers\Form\Number( 'renew_cycles', 1, TRUE, array( 'min' => 1 ) ) );
                if (
$values = $form->values() )
                {
                    foreach (
$values['purchases_to_renew'] as $purchase )
                    {
                       
$invoice->addItem( \IPS\nexus\Invoice\Item\Renewal::create( $purchase, $values['renew_cycles'] ) );
                    }
                   
$data['items'] = $invoice->items->getArrayCopy();
                   
$_SESSION[ 'wizard-' . md5( $url ) . '-data' ] = $data;
                    \
IPS\Output::i()->redirect( $url );
                }
                return
$form;
            }
                       
           
$itemTypes = \IPS\Application::allExtensions( 'nexus', 'Item', TRUE, NULL, NULL, FALSE );
            if ( isset( \
IPS\Request::i()->add ) and isset( $itemTypes[ \IPS\Request::i()->add ] ) )
            {
               
$class = $itemTypes[ \IPS\Request::i()->add ];
               
               
$formUrl = $url->setQueryString( 'add', \IPS\Request::i()->add );                
               
$form = new \IPS\Helpers\Form( 'add', 'invoice_add_item', $formUrl );
                if (
method_exists( $class, 'formSecondStep' ) )
                {
                   
$form->ajaxOutput = TRUE;
                }
               
$class::form( $form, $invoice );
                if (
$values = $form->values() or ( method_exists( $class, 'formSecondStep' ) and isset( \IPS\Request::i()->firstStep ) ) )
                {
                    if (
method_exists( $class, 'formSecondStep' ) )
                    {
                       
$firstStepValues = isset( \IPS\Request::i()->firstStep ) ? urldecode( \IPS\Request::i()->firstStep ) : json_encode( array_map( function( $val )
                        {
                            return (
$val instanceof \IPS\Node\Model ) ? $val->_id : $val;
                        },
$values ) );                        
                       
$secondStepForm = new \IPS\Helpers\Form( 'add2', 'invoice_add_item', $formUrl->setQueryString( 'firstStep', $firstStepValues ) );
                       
$secondStepForm->ajaxOutput = TRUE;
                       
$secondStepForm->hiddenValues['firstStep'] = $firstStepValues;
                        if (
$class::formSecondStep( json_decode( $firstStepValues, TRUE ), $secondStepForm, $invoice ) )
                        {
                            if (
$secondStepValues = $secondStepForm->values() )
                            {
                               
$item = $class::createFromForm( $secondStepValues, $invoice );
                                if(
is_array( $item ) )
                                {
                                    foreach (
$item as $i )
                                    {
                                       
$invoice->addItem( $i );
                                    }
                                }
                                else
                                {
                                   
$invoice->addItem( $item );
                                }
                               
$data['items'] = $invoice->items->getArrayCopy();
                               
$_SESSION[ 'wizard-' . md5( $url ) . '-data' ] = $data;
                                \
IPS\Output::i()->redirect( $url );
                                return;
                            }
                            return
$secondStepForm;
                        }
                    }

                   
$item = $class::createFromForm( $values, $invoice );
                    if(
is_array( $item ) )
                    {
                        foreach (
$item as $i )
                        {
                           
$invoice->addItem( $i );
                        }
                    }
                    else
                    {
                       
$invoice->addItem( $item );
                    }

                   
$data['items'] = $invoice->items->getArrayCopy();
                   
$_SESSION[ 'wizard-' . md5( $url ) . '-data' ] = $data;                    
                    \
IPS\Output::i()->redirect( $url );
                }
               
                if ( \
IPS\Request::i()->isAjax() )
                {
                    \
IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->blankTemplate( $form ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
                }
                else
                {
                    \
IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, $form, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
                }
                return;
            }
           
            return \
IPS\Theme::i()->getTemplate('invoices')->generate( $invoice->summary(), $itemTypes, $url );                    
           
        };
       
       
/* Display */
       
\IPS\Output::i()->output = new \IPS\Helpers\Wizard( $steps, $url, ( !isset( \IPS\Request::i()->add ) and !isset( \IPS\Request::i()->addRenewal ) ) );
    }
   
   
/**
     * Product Tree (AJAX)
     *
     * @return    void
     */
   
public function productTree()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_add' );
       
       
$output = '';
        foreach( \
IPS\nexus\Package\Group::load( \IPS\Request::i()->id )->children() as $child )
        {
            if (
$child instanceof \IPS\nexus\Package\Group )
            {
               
$output .= \IPS\Theme::i()->getTemplate('invoices')->packageSelectorGroup( $child );
            }
            else
            {
               
$output .= \IPS\Theme::i()->getTemplate('invoices')->packageSelectorProduct( $child );
            }
        }
       
        \
IPS\Output::i()->json( $output );
    }
   
   
/**
     * Settings
     *
     * @return    void
     */
   
protected function settings()
    {
        \
IPS\Dispatcher::i()->checkAcpPermission( 'invoices_settings' );
       
       
$form = new \IPS\Helpers\Form;
       
$form->addheader('invoice_flow');
       
$form->addMessage('invoice_flow_visualise');
       
$form->add( new \IPS\Helpers\Form\Number( 'cm_invoice_generate', \IPS\Settings::i()->cm_invoice_generate, FALSE, array( 'min' => 1 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('cm_invoice_generate_suffix') ) );
       
$form->add( new \IPS\Helpers\Form\Number( 'cm_invoice_warning', \IPS\Settings::i()->cm_invoice_warning, FALSE, array( 'unlimited' => 0, 'unlimitedLang' => 'never' ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('cm_invoice_warning_suffix') ) );
       
$form->add( new \IPS\Helpers\Form\Number( 'cm_invoice_expireafter', \IPS\Settings::i()->cm_invoice_expireafter, FALSE, array( 'unlimited' => 0 ), NULL, NULL, \IPS\Member::loggedIn()->language()->addToStack('days') ) );
       
$form->addHeader('invoice_layout');
       
$form->addMessage('invoice_layout_blurb');
       
$form->add( new \IPS\Helpers\Form\Editor( 'nexus_invoice_header', \IPS\Settings::i()->nexus_invoice_header, FALSE, array( 'app' => 'nexus', 'key' => 'Admin', 'autoSaveKey'    => 'nexus-invoice-header', 'attachIds' => array( NULL, NULL, 'invoice-header' ), 'minimize' => 'nexus_invoice_header_placeholder'  ) ) );
       
$form->add( new \IPS\Helpers\Form\Editor( 'nexus_invoice_footer', \IPS\Settings::i()->nexus_invoice_footer, FALSE, array( 'app' => 'nexus', 'key' => 'Admin', 'autoSaveKey'    => 'nexus-invoice-footer', 'attachIds' => array( NULL, NULL, 'invoice-footer' ), 'minimize' => 'nexus_invoice_footer_placeholder'  ) ) );
       
        if (
$values = $form->values() )
        {
            \
IPS\Db::i()->update( 'core_tasks', array( 'enabled' => (int) (bool) $values['cm_invoice_expireafter'] ), "`key`='expireInvoices'" );
           
           
$form->saveAsSettings();
            \
IPS\Session::i()->log( 'acplogs__invoice_settings' );
            \
IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=nexus&module=payments&controller=invoices&do=settings'), 'saved' );
        }
       
        \
IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack('invoice_settings');
        \
IPS\Output::i()->output = $form;
    }
   
   
/**
     * Redirect
     *
     * @param    \IPS\nexus\Invoice    $invoice    The invoice
     * @return    void
     */
   
protected function _redirect( \IPS\nexus\Invoice $invoice )
    {
        if ( isset( \
IPS\Request::i()->r ) )
        {
            switch (
mb_substr( \IPS\Request::i()->r, 0, 1 ) )
            {
                case
'v':
                    \
IPS\Output::i()->redirect( $invoice->acpUrl() );
                    break;
               
                case
'p':
                    try
                    {
                        \
IPS\Output::i()->redirect( \IPS\nexus\Purchase::load( mb_substr( \IPS\Request::i()->r, 2 ) )->acpUrl() );
                        break;
                    }
                    catch ( \
OutOfRangeException $e ) {}
               
                case
'c':
                    \
IPS\Output::i()->redirect( $invoice->member->acpUrl() );
                    break;
               
                case
't':
                    \
IPS\Output::i()->redirect( \IPS\Http\Url::internal('app=nexus&module=payments&controller=invoices') );
                    break;
                   
            }
        }
       
        \
IPS\Output::i()->redirect( $invoice->acpUrl() );
    }
}