/**
  * handles the IPN. Gets the gateways model to call the currently-used gateway,
  * and handle the IPN in its desired manner.
  * Expects that the IPN sent a POST or GET requests with parameters 'e_reg_url_link'
  * (which is a registration's REG_url_link value) and 'ee_gateway' (which is the
  * gateway_name of the gateway sending the IPN. Eg, 'PayPal_Standard').
  * Maybe we'll want to die afterwards? For now I'll just let the gateway handle that
  */
 function handle_ipn()
 {
     //in the REQUEST, we expect  reg_url_link, ee_gateway. If not,
     //it must not be an Instant Payment Notification...
     if (isset($_GET['e_reg_url_link']) && isset($_GET['ee_gateway']) || isset($_POST['e_reg_url_link']) && isset($_POST['ee_gateway'])) {
         $transaction = $this->_TXN->get_transaction_from_reg_url_link($_GET['e_reg_url_link']);
         $indicated_gateway = array_key_exists('ee_gateway', $_GET) ? $_GET['ee_gateway'] : $_POST['ee_gateway'];
         $this->_GATEWAYS->set_selected_gateway($indicated_gateway);
         $this->_GATEWAYS->handle_ipn_for_transaction($transaction);
     }
 }
 public function setUp()
 {
     EEM_Payment::reset();
     EEM_Transaction::reset();
     //		EEM_Payment_Method::reset();
     parent::setUp();
 }
 /**
  * @group 7965
  */
 function test_delete_junk_transactions()
 {
     $old_txn_count = EEM_Transaction::instance()->count();
     $pretend_bot_creations = 9;
     $pretend_real_recent_txns = 3;
     $pretend_real_good_txns = 5;
     $this->factory->transaction->create_many($pretend_bot_creations, array('TXN_timestamp' => time() - WEEK_IN_SECONDS * 2, 'STS_ID' => EEM_Transaction::failed_status_code));
     $this->factory->transaction->create_many($pretend_real_recent_txns, array('TXN_timestamp' => time() - EE_Registry::instance()->SSN->lifespan() + MINUTE_IN_SECONDS, 'STS_ID' => EEM_Transaction::failed_status_code));
     $this->factory->transaction->create_many($pretend_real_good_txns, array('STS_ID' => EEM_Transaction::abandoned_status_code));
     $num_deleted = EEM_Transaction::instance()->delete_junk_transactions();
     $this->assertEquals($pretend_bot_creations, $num_deleted);
 }
 /**
  *		This function is a singleton method used to instantiate the Espresso_model object
  *
  *		@access public
  *		@param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).  Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
  *		@return EEM_Transaction instance
  */
 public static function instance($timezone = NULL)
 {
     // check if instance of Espresso_model already exists
     if (self::$_instance === NULL) {
         // instantiate Espresso_model
         self::$_instance = new self($timezone);
     }
     //we might have a timezone set, let set_timezone decide what to do with it
     self::$_instance->set_timezone($timezone);
     // Espresso_model object
     return self::$_instance;
 }
 function test_save_autoincrement_pk()
 {
     //test saving something with an auto-increment PK
     $t = EE_Transaction::new_instance();
     $id = $t->save();
     $this->assertNotNull($id);
     $this->assertEquals($t->ID(), $id);
     $t2 = EEM_Transaction::instance()->get_one_by_ID($id);
     $this->assertEquals($id, $t2->ID());
     //and check that its correctly saved to the model's entity map
     $existing_t_in_entity_map = EEM_Transaction::instance()->get_from_entity_map($id);
     $this->assertInstanceOf('EE_Transaction', $existing_t_in_entity_map);
 }
 function test_generate_restrictions__basic_only()
 {
     //currently transactions only have the basic cap
     //if they get an 'ee_edit_others_transactions' cap, this test will need updating
     $generator = new EE_Restriction_Generator_Protected();
     $generator->_construct_finalize(EEM_Transaction::instance(), EEM_Base::caps_read);
     $restrictions = $generator->generate_restrictions();
     foreach ($restrictions as $default_where_conditions) {
         $default_where_conditions->_finalize_construct(EEM_Registration::instance());
     }
     $this->assertArrayHasKey('ee_read_transactions', $restrictions);
     $this->assertInstanceOf('EE_Return_None_Where_Conditions', $restrictions['ee_read_transactions']);
     $this->assertEquals(1, count($restrictions));
 }
 /**
  * Callback for FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg for adding
  * helpful title.
  *
  * @param string $content Current content
  * @param string $page_slug Page slug for page
  * @param array $req_data Incoming request data
  * @param string $req_action 'action' value for page
  *
  * @return string   If correct page and conditions are met, the new string. Otherwise existing string.
  */
 public static function before_events_list_table_content($content, $page_slug, $req_data, $req_action)
 {
     if ($page_slug !== 'espresso_events' || $req_action !== 'default' || empty($req_data['TXN_ID'])) {
         return $content;
     }
     $transaction = EEM_Transaction::instance()->get_one_by_ID($req_data['TXN_ID']);
     if ($transaction instanceof EE_Transaction) {
         $query_args = array('page' => 'espresso_transactions', 'action' => 'view_transaction', 'TXN_ID' => $req_data['TXN_ID']);
         EE_Registry::instance()->load_helper('URL');
         $url = EEH_URL::add_query_args_and_nonce($query_args, admin_url('admin.php'));
         $link_text = '<a href="' . $url . '">' . $transaction->ID() . '</a>';
         $content .= '<h2>' . sprintf(__('Events Registered for in Transaction # %s', 'event_espresso'), $link_text) . '</h2>';
     }
     return $content;
 }
 /**
  * This generates the dummy relation objects for use in a new registration.
  *
  * @since 4.3.0
  *
  * @param array $args
  */
 private function _set_new_relations($args)
 {
     //transaction
     $this->_transaction = empty($args['TXN_ID']) ? $this->factory->transaction->create() : EEM_Transaction::instance()->get_one_by_ID($args['TXN_ID']);
     $this->_transaction = empty($this->_transaction) ? $this->factory->transaction->create() : $this->_transaction;
     //ticket
     $this->_ticket = empty($args['TKT_ID']) ? $this->factory->ticket_chained->create() : EEM_Ticket::instance()->get_one_by_ID($args['TKT_ID']);
     $this->_ticket = empty($this->_ticket) ? $this->factory->ticket_chained->create() : $this->_ticket;
     //attendee
     $this->_attendee = empty($args['ATT_ID']) ? $this->factory->attendee->create() : EEM_Attendee::instance()->get_one_by_ID($args['ATT_ID']);
     $this->_attendee = empty($this->_attendee) ? $this->factory->attendee->create() : $this->_attendee;
     //status
     $this->_status = empty($arg['STS_ID']) ? $this->factory->status->create(array('STS_ID' => EEM_Registration::status_id_pending_payment, 'STS_type' => 'registration', 'STS_code' => 'PENDING_PAYMENT')) : EEM_Status::instance()->get_one_by_ID($args['STS_ID']);
     $this->_status = empty($this->_status) ? $this->factory->status->create(array('STS_ID' => EEM_Registration::status_id_pending_payment, 'STS_type' => 'registration', 'STS_code' => 'PENDING_PAYMENT')) : $this->_status;
 }
 /**
  * Updates the transaction according to the payment info
  * @param EE_Transaction or int $transaction the transaction to update, or its ID. Cannot be null.
  * @param EE_Payment or int $payment the payment just made or its ID. If empty that's actually OK. It just means no payment has been made.
  * @return boolean success
  */
 public function update_transaction_with_payment($transaction, $payment)
 {
     if (empty($transaction)) {
         return false;
     }
     $transaction = $this->_TXN->ensure_is_obj($transaction);
     /* @var $transaction EE_transaction */
     //now, if the payment's empty, we're going to update the transaction accordingly
     if (empty($payment)) {
         $transaction->set_status(EEM_Transaction::incomplete_status_code);
         $transaction->update_extra_meta('gateway', $this->_gateway_name);
         do_action('AHEE__EE_Gateway__update_transaction_with_payment__no_payment', $transaction);
     } else {
         $payment = $this->_PAY->ensure_is_obj($payment);
         //ok, now process the transaction according to the payment
         $transaction->update_based_on_payments();
         $transaction->update_extra_meta('gateway', $this->_gateway_name);
         do_action('AHEE__EE_Gateway__update_transaction_with_payment__done', $transaction, $payment);
     }
     $transaction->save();
     $transaction->finalize();
     return true;
 }
 /**
  *  get_transactions
  *    get transactions for given parameters (used by list table)
  *
  * @param  int     $perpage how many transactions displayed per page
  * @param  boolean $count return the count or objects
  * @param string   $view
  * @return mixed int = count || array of transaction objects
  */
 public function get_transactions($perpage, $count = FALSE, $view = '')
 {
     $TXN = EEM_Transaction::instance();
     $start_date = isset($this->_req_data['txn-filter-start-date']) ? wp_strip_all_tags($this->_req_data['txn-filter-start-date']) : date('D M j, Y', strtotime('-10 year'));
     $end_date = isset($this->_req_data['txn-filter-end-date']) ? wp_strip_all_tags($this->_req_data['txn-filter-end-date']) : date('D M j, Y');
     //make sure our timestamps start and end right at the boundaries for each day
     $start_date = date('Y-m-d', strtotime($start_date)) . ' 00:00:00';
     $end_date = date('Y-m-d', strtotime($end_date)) . ' 23:59:59';
     //convert to timestamps
     $start_date = strtotime($start_date);
     $end_date = strtotime($end_date);
     //makes sure start date is the lowest value and vice versa
     $start_date = min($start_date, $end_date);
     $end_date = max($start_date, $end_date);
     //set orderby
     $this->_req_data['orderby'] = !empty($this->_req_data['orderby']) ? $this->_req_data['orderby'] : '';
     switch ($this->_req_data['orderby']) {
         case 'TXN_ID':
             $orderby = 'TXN_ID';
             break;
         case 'ATT_fname':
             $orderby = 'Registration.Attendee.ATT_fname';
             break;
         case 'event_name':
             $orderby = 'Registration.Event.EVT_name';
             break;
         default:
             //'TXN_timestamp'
             $orderby = 'TXN_timestamp';
     }
     $sort = isset($this->_req_data['order']) && !empty($this->_req_data['order']) ? $this->_req_data['order'] : 'DESC';
     $current_page = isset($this->_req_data['paged']) && !empty($this->_req_data['paged']) ? $this->_req_data['paged'] : 1;
     $per_page = isset($perpage) && !empty($perpage) ? $perpage : 10;
     $per_page = isset($this->_req_data['perpage']) && !empty($this->_req_data['perpage']) ? $this->_req_data['perpage'] : $per_page;
     $offset = ($current_page - 1) * $per_page;
     $limit = array($offset, $per_page);
     $_where = array('TXN_timestamp' => array('BETWEEN', array($start_date, $end_date)), 'Registration.REG_count' => 1);
     if (isset($this->_req_data['EVT_ID'])) {
         $_where['Registration.EVT_ID'] = $this->_req_data['EVT_ID'];
     }
     if (isset($this->_req_data['s'])) {
         $search_string = '%' . $this->_req_data['s'] . '%';
         $_where['OR'] = array('Registration.Event.EVT_name' => array('LIKE', $search_string), 'Registration.Event.EVT_desc' => array('LIKE', $search_string), 'Registration.Event.EVT_short_desc' => array('LIKE', $search_string), 'Registration.Attendee.ATT_fname' => array('LIKE', $search_string), 'Registration.Attendee.ATT_lname' => array('LIKE', $search_string), 'Registration.Attendee.ATT_short_bio' => array('LIKE', $search_string), 'Registration.Attendee.ATT_email' => array('LIKE', $search_string), 'Registration.Attendee.ATT_address' => array('LIKE', $search_string), 'Registration.Attendee.ATT_address2' => array('LIKE', $search_string), 'Registration.Attendee.ATT_city' => array('LIKE', $search_string), 'Registration.REG_final_price' => array('LIKE', $search_string), 'Registration.REG_code' => array('LIKE', $search_string), 'Registration.REG_count' => array('LIKE', $search_string), 'Registration.REG_group_size' => array('LIKE', $search_string), 'Registration.Ticket.TKT_name' => array('LIKE', $search_string), 'Registration.Ticket.TKT_description' => array('LIKE', $search_string), 'Payment.PAY_source' => array('LIKE', $search_string), 'Payment.Payment_Method.PMD_name' => array('LIKE', $search_string), 'TXN_session_data' => array('LIKE', $search_string));
     }
     //failed transactions
     $failed = !empty($this->_req_data['status']) && $this->_req_data['status'] == 'failed' && !$count || $count && $view == 'failed' ? TRUE : FALSE;
     $abandoned = !empty($this->_req_data['status']) && $this->_req_data['status'] == 'abandoned' && !$count || $count && $view == 'abandoned' ? TRUE : FALSE;
     if ($failed) {
         $_where['STS_ID'] = EEM_Transaction::failed_status_code;
     } else {
         if ($abandoned) {
             $_where['STS_ID'] = EEM_Transaction::abandoned_status_code;
         } else {
             $_where['STS_ID'] = array('!=', EEM_Transaction::failed_status_code);
             $_where['STS_ID*'] = array('!=', EEM_Transaction::abandoned_status_code);
         }
     }
     $query_params = array($_where, 'order_by' => array($orderby => $sort), 'limit' => $limit);
     $transactions = $count ? $TXN->count(array($_where), 'TXN_ID', TRUE) : $TXN->get_all($query_params);
     return $transactions;
 }
 /**
  * Checks that we can correctly apply backend read caps where there is only
  * one cap controlling access to the model
  * @group model_caps
  */
 public function test_get_all__caps_admin_read__basic()
 {
     $this->assertEquals(0, EEM_Transaction::instance()->count());
     $this->new_typical_transaction();
     $current_user = $this->_ensure_current_user_set();
     //let's test first on transactions, which just have a single cap controlling access
     //which the current user doesn't have so nothing should be found
     $this->assertEquals(0, EEM_Transaction::instance()->count(array('caps' => EEM_Base::caps_read_admin)));
     //now give the user permission to access transactions and make sure he can
     $current_user->add_cap('ee_read_transactions');
     $this->assertEquals(EEM_Transaction::instance()->count(), EEM_Transaction::instance()->count(array('caps' => EEM_Base::caps_read_admin)));
 }
 /**
  * Data that has been stored in persistent storage that was prepped by _convert_data_for_persistent_storage
  * can be sent into this method and converted back into the format used for instantiating with this data handler.
  *
  * @param array  $data
  *
  * @return array
  */
 public static function convert_data_from_persistent_storage($data)
 {
     $prepped_data = array(0 => isset($data['Transaction']) ? EEM_Transaction::instance()->get_one_by_ID($data['Transaction']) : null, 1 => isset($data['Payment']) ? EEM_Payment::instance()->get_one_by_ID($data['Payment']) : null, 2 => isset($data['filter']) ? $data['filter'] : null);
     return $prepped_data;
 }
 /**
  * _validate_return
  *
  * @access private
  * @return bool
  */
 private function _validate_offsite_return()
 {
     $TXN_ID = (int) EE_Registry::instance()->REQ->get('spco_txn', 0);
     if ($TXN_ID != $this->checkout->transaction->ID()) {
         // Houston... we might have a problem
         $invalid_TXN = false;
         // first gather some info
         $valid_TXN = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
         $primary_registrant = $valid_TXN instanceof EE_Transaction ? $valid_TXN->primary_registration() : null;
         // let's start by retrieving the cart for this TXN
         $cart = EE_Cart::get_cart_from_txn($this->checkout->transaction);
         if ($cart instanceof EE_Cart) {
             // verify that the current cart has tickets
             $tickets = $cart->get_tickets();
             if (empty($tickets)) {
                 $invalid_TXN = true;
             }
         } else {
             $invalid_TXN = true;
         }
         $valid_TXN_SID = $primary_registrant instanceof EE_Registration ? $primary_registrant->session_ID() : null;
         // validate current Session ID and compare against valid TXN session ID
         if (EE_Session::instance()->id() === null) {
             $invalid_TXN = true;
         } else {
             if (EE_Session::instance()->id() === $valid_TXN_SID) {
                 // WARNING !!!
                 // this could be PayPal sending back duplicate requests (ya they do that)
                 // or it **could** mean someone is simply registering AGAIN after having just done so
                 // so now we need to determine if this current TXN looks valid or not
                 /** @type EE_Transaction_Processor $transaction_processor */
                 $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
                 // has this step even been started ?
                 if ($transaction_processor->reg_step_completed($this->checkout->transaction, $this->slug() === false)) {
                     // really? you're half way through this reg step, but you never started it ?
                     $invalid_TXN = true;
                 }
             }
         }
         if ($invalid_TXN) {
             // is the valid TXN completed ?
             if ($valid_TXN instanceof EE_Transaction) {
                 /** @type EE_Transaction_Processor $transaction_processor */
                 $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
                 // has this step even been started ?
                 $reg_step_completed = $transaction_processor->reg_step_completed($valid_TXN, $this->slug());
                 if ($reg_step_completed !== false && $reg_step_completed !== true) {
                     // so it **looks** like this is a double request from PayPal
                     // so let's try to pick up where we left off
                     $this->checkout->transaction = $valid_TXN;
                     $this->checkout->refresh_all_entities(true);
                     return;
                 }
             }
             // you appear to be lost?
             $this->_redirect_wayward_request($primary_registrant);
         }
     }
 }
 /**
  * return the transaction object for a given transaction ID.
  *
  * @since 4.3.0
  *
  * @param int $TXN_ID the transaction id for the transaction to attempt to retrieve
  *
  * @return mixed null|EE_Transaction
  */
 public function get_object_by_id($TXN_ID)
 {
     return EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
 }
    /**
     * Deletes "junk" transactions that were probably added by bots. There might be TONS
     * of these, so we are very careful to NOT select (which the models do even when deleting),
     * and so we only use wpdb directly and NOT do any joins.
     * The downside to this approach is that is addons are listening for object deletions
     * on EEM_Base::delete() they won't be notified of this.
     * @global WPDB $wpdb
     * @return mixed
     */
    public function delete_junk_transactions()
    {
        /** @type WPDB $wpdb */
        global $wpdb;
        $deleted = false;
        $time_to_leave_alone = apply_filters('FHEE__EEM_Transaction__delete_junk_transactions__time_to_leave_alone', WEEK_IN_SECONDS);
        /**
         * This allows code to filter the query arguments used for retrieving the transaction IDs to delete.
         * Useful for plugins that want to exclude transactions matching certain query parameters.
         * The query parameters should be in the format accepted by the EEM_Base model queries.
         */
        $ids_query = apply_filters('FHEE__EEM_Transaction__delete_junk_transactions__initial_query_args', array(0 => array('STS_ID' => EEM_Transaction::failed_status_code, 'TXN_timestamp' => array('<', time() - $time_to_leave_alone))), $time_to_leave_alone);
        /**
         * This filter is for when code needs to filter the list of transaction ids that represent transactions
         * about to be deleted based on some other criteria that isn't easily done via the query args filter.
         */
        $txn_ids = apply_filters('FHEE__EEM_Transaction__delete_junk_transactions__transaction_ids_to_delete', EEM_Transaction::instance()->get_col($ids_query, 'TXN_ID'), $time_to_leave_alone);
        //now that we have the ids to delete
        if (!empty($txn_ids) && is_array($txn_ids)) {
            // first, make sure these TXN's are removed the "ee_locked_transactions" array
            EEM_Transaction::unset_locked_transactions($txn_ids);
            // let's get deletin'...
            // Why no wpdb->prepare?  Because the data is trusted.
            // We got the ids from the original query to get them FROM
            // the db (which is sanitized) so no need to prepare them again.
            $query = '
				DELETE
				FROM ' . $this->table() . '
				WHERE
					TXN_ID IN ( ' . implode(",", $txn_ids) . ')';
            $deleted = $wpdb->query($query);
        }
        if ($deleted) {
            /**
             * Allows code to do something after the transactions have been deleted.
             */
            do_action('AHEE__EEM_Transaction__delete_junk_transactions__successful_deletion', $txn_ids);
        }
        return $deleted;
    }
 public static function clean_out_junk_transactions()
 {
     if (EE_Maintenance_Mode::instance()->models_can_query()) {
         EEM_Transaction::instance('')->delete_junk_transactions();
         EEM_Registration::instance('')->delete_registrations_with_no_transaction();
         EEM_Line_Item::instance('')->delete_line_items_with_no_transaction();
     }
 }
 function test_wp_user_field_name()
 {
     $this->assertEquals('EVT_wp_user', EEM_Event::instance()->wp_user_field_name());
     $this->assertEquals('Registration.Event.EVT_wp_user', EEM_Transaction::instance()->wp_user_field_name());
     $this->assertEquals('TKT_wp_user', EEM_Ticket::instance()->wp_user_field_name());
 }
 /**
  *    _payments_and_amount_owing_rows
  *
  * @param EE_Line_Item $line_item
  * @param array        $options
  * @return mixed
  */
 private function _payments_and_amount_owing_rows(EE_Line_Item $line_item, $options = array())
 {
     $html = '';
     $owing = $line_item->total();
     $transaction = EEM_Transaction::instance()->get_one_by_ID($line_item->TXN_ID());
     if ($transaction instanceof EE_Transaction) {
         $registration_payments = array();
         $registrations = !empty($options['registrations']) ? $options['registrations'] : $transaction->registrations();
         foreach ($registrations as $registration) {
             if ($registration instanceof EE_Registration && $registration->owes_monies_and_can_pay()) {
                 $registration_payments = $registration_payments + $registration->registration_payments();
             }
         }
         if (!empty($registration_payments)) {
             foreach ($registration_payments as $registration_payment) {
                 if ($registration_payment instanceof EE_Registration_Payment) {
                     $owing = $owing - $registration_payment->amount();
                     $payment = $registration_payment->payment();
                     if ($payment instanceof EE_Payment) {
                         $payment_desc = sprintf(__('Payment%1$s Received: %2$s', 'event_espresso'), $payment->txn_id_chq_nmbr() != '' ? ' <span class="small-text">(#' . $payment->txn_id_chq_nmbr() . ')</span> ' : '', $payment->timestamp());
                     } else {
                         $payment_desc = '';
                     }
                     // start of row
                     $html .= EEH_HTML::tr('', '', 'total_tr odd');
                     // payment desc
                     $html .= EEH_HTML::td($payment_desc, '', '', '', ' colspan="3"');
                     // total td
                     $html .= EEH_HTML::td(EEH_Template::format_currency($registration_payment->amount(), false, false), '', 'total jst-rght');
                     // end of row
                     $html .= EEH_HTML::trx();
                 }
             }
             if ($line_item->total()) {
                 // start of row
                 $html .= EEH_HTML::tr('', '', 'total_tr odd');
                 // total td
                 $html .= EEH_HTML::td(__('Amount Owing', 'event_espresso'), '', 'total_currency total jst-rght', '', ' colspan="3"');
                 // total td
                 $html .= EEH_HTML::td(EEH_Template::format_currency($owing, false, false), '', 'total jst-rght');
                 // end of row
                 $html .= EEH_HTML::trx();
             }
         }
     }
     $this->_grand_total = $owing;
     return $html;
 }
 /**
  * Handles an instant payment notification when the transaction is known (by default).
  * @param array $req_data
  * @param EE_Transaction $transaction
  * @return EE_Payment
  * @throws EE_Error
  */
 public function handle_ipn($req_data, $transaction)
 {
     $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
     if (!$this->_gateway instanceof EE_Offsite_Gateway) {
         throw new EE_Error(sprintf(__("Could not handle IPN because '%s' is not an offsite gateway", "event_espresso"), print_r($this->_gateway, TRUE)));
     }
     $payment = $this->_gateway->handle_payment_update($req_data, $transaction);
     return $payment;
 }
 /**
  * Deletes all registrations with no transactions. Note that this needs to be very efficient
  * and so it uses wpdb directly
  * @global WPDB $wpdb
  * @return int number deleted
  */
 public function delete_registrations_with_no_transaction()
 {
     /** @type WPDB $wpdb */
     global $wpdb;
     return $wpdb->query('DELETE r FROM ' . $this->table() . ' r LEFT JOIN ' . EEM_Transaction::instance()->table() . ' t ON r.TXN_ID = t.TXN_ID WHERE t.TXN_ID IS NULL');
 }
 /**
  * The purpose of this method is to just initialize SPCO for manual admin registration handling
  *
  * Note, it is fully expected that eventually things will be abstracted a bit more conveniently for the Admin usage of SPCO but this is a quick and dirty method to get things implemented with as much DRY as possible.
  *
  * @access public
  * @return void
  */
 public function init_for_admin()
 {
     // load classes
     EE_Registry::instance()->load_model('Gateways');
     $this->_cart = EE_Registry::instance()->load_core('Cart');
     if (empty(EE_Registry::instance()->REQ)) {
         EE_Registry::instance()->load_core('Request_Handler');
     }
     $this->_current_step = 'attendee_information';
     $this->_transaction = EE_Registry::instance()->SSN->get_session_data('transaction');
     // verify transaction
     if ($this->_transaction instanceof EE_Transaction) {
         // but if this transaction has already been saved to the db... then let's pull that
         $ID = $this->_transaction->ID();
         if ($ID) {
             if (!($this->_transaction = EEM_Transaction::instance()->get_one_by_ID($ID))) {
                 EE_Error::add_error(__('The Transaction could not be retrieved from the db when attempting to process your registration information', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
                 return;
             }
         }
     } else {
         $this->_initialize_transaction();
     }
     // and verify registrations have been set (first check cached REGs)
     if ($this->_transaction->registrations(array(), TRUE) == NULL) {
         // then check the db
         if ($this->_transaction->registrations() == NULL) {
             $this->_initialize_registrations();
         }
     }
     if (!defined('SPCO_TEMPLATES_PATH')) {
         self::set_definitions();
     }
     $this->set_templates();
 }
 /**
  * 	_payments_and_amount_owing_rows
  *
  * @param EE_Line_Item $line_item
  * @return mixed
  */
 private function _payments_and_amount_owing_rows(EE_Line_Item $line_item)
 {
     $html = '';
     $transaction = EEM_Transaction::instance()->get_one_by_ID($line_item->TXN_ID());
     if ($transaction instanceof EE_Transaction) {
         $payments = $transaction->approved_payments();
         if (!empty($payments)) {
             foreach ($payments as $payment) {
                 if ($payment instanceof EE_Payment) {
                     //$owing = $owing - $payment->amount();
                     $payment_desc = sprintf(__('Payment%1$s Received: %2$s', 'event_espresso'), $payment->txn_id_chq_nmbr() != '' ? ' <span class="small-text">(#' . $payment->txn_id_chq_nmbr() . ')</span> ' : '', $payment->timestamp());
                     // start of row
                     $html .= EEH_HTML::tr('', '', 'total_tr odd');
                     // payment desc
                     $html .= EEH_HTML::td($payment_desc, '', '', '', ' colspan="3"');
                     // total td
                     $html .= EEH_HTML::td(EEH_Template::format_currency($payment->amount(), false, false), '', 'total jst-rght');
                     // end of row
                     $html .= EEH_HTML::trx();
                 }
             }
             //if ( $line_item->total() ) {
             //	// start of row
             //	$html .= EEH_HTML::tr( '', '', 'total_tr odd' );
             //	// total td
             //	$html .= EEH_HTML::td( __('Amount Owing', 'event_espresso'), '',  'total_currency total jst-rght',  '',  ' colspan="3"' );
             //	// total td
             //	$html .= EEH_HTML::td( EEH_Template::format_currency( $this->grand_total(), false, false ), '',  'total jst-rght' );
             //	// end of row
             //	$html .= EEH_HTML::trx();
             //}
         }
     }
     return $html;
 }
 /**
  * _revenue_per_event_report
  * generates Business Report showing total revenue per event
  *
  * @access private
  * @param string $period
  * @return int
  */
 private function _revenue_per_event_report($period = '-1 month')
 {
     $report_ID = 'txn-admin-revenue-per-event-report-dv';
     $report_JS = 'espresso_txn_admin_revenue_per_event';
     wp_enqueue_script($report_JS, TXN_CAF_ASSETS_URL . $report_JS . '_report.js', array('jqplot-all'), '1.0', TRUE);
     $TXN = EEM_Transaction::instance();
     $results = $TXN->get_revenue_per_event_report($period);
     //EEH_Debug_Tools::printr( $results, '$registrations_per_event' );
     $revenue = array();
     $results = (array) $results;
     foreach ($results as $result) {
         $event_name = stripslashes(html_entity_decode($result->event_name, ENT_QUOTES, 'UTF-8'));
         $event_name = wp_trim_words($event_name, 5, '...');
         $revenue[] = array($event_name, (double) $result->revenue);
     }
     $span = $period == 'week' ? 9 : 33;
     $report_title = __('Total Revenue per Event');
     $report_params = array('title' => $report_title, 'id' => $report_ID, 'revenue' => $revenue, 'span' => $span, 'width' => ceil(900 / $span), 'noTxnMsg' => sprintf(__('<h2>%s</h2><p>There are currently no transaction records in the last month for this report.</p>', 'event_espresso'), $report_title));
     wp_localize_script($report_JS, 'revenuePerEvent', $report_params);
     return $report_ID;
 }
    /**
     * Deletes line items with no transaction who have passed the transaction cutoff time.
     * This needs to be very efficient
     * because if there are spam bots afoot there will be LOTS of line items
     * @return int count of how many deleted
     */
    public function delete_line_items_with_no_transaction()
    {
        /** @type WPDB $wpdb */
        global $wpdb;
        $time_to_leave_alone = apply_filters('FHEE__EEM_Line_Item__delete_line_items_with_no_transaction__time_to_leave_alone', WEEK_IN_SECONDS);
        $query = $wpdb->prepare('DELETE li
				FROM ' . $this->table() . ' li
				LEFT JOIN ' . EEM_Transaction::instance()->table() . ' t ON li.TXN_ID = t.TXN_ID
				WHERE t.TXN_ID IS NULL AND li.LIN_timestamp < %s', gmdate('Y-m-d H:i:s', time() - $time_to_leave_alone));
        return $wpdb->query($query);
    }
Esempio n. 25
0
 /**
  * Update lead status of the specified payment
  *
  * @param Pronamic_Pay_Payment $payment
  * @param bool                 $can_redirect
  */
 public function update_status_unknown_to_success(Pronamic_Pay_Payment $payment, $can_redirect = false)
 {
     if (!(version_compare(EVENT_ESPRESSO_VERSION, '4', '>=') && version_compare(EVENT_ESPRESSO_VERSION, '4.6', '<'))) {
         return;
     }
     // Eevent Espresso 4.0 to 4.6
     $gateway = EEM_Gateways::instance()->get_gateway('pronamic_pay_ideal');
     if ($gateway) {
         $transaction_id = $payment->get_source_id();
         // @see https://github.com/eventespresso/event-espresso-core/blob/4.2.2.reg/admin_pages/transactions/Transactions_Admin_Page.core.php#L332-L337
         $transaction_model = EEM_Transaction::instance();
         $transaction = $transaction_model->get_one_by_ID($transaction_id);
         global $pronamic_payment, $pronamic_url;
         $pronamic_payment = $payment;
         $gateway->handle_ipn_for_transaction($transaction);
         unset($pronamic_payment);
         // Redirect URL
         if ($can_redirect) {
             wp_redirect($pronamic_url, 303);
             exit;
         }
     }
 }
 /**
  * This should be called each time there may have been an update to a
  * payment on a transaction (ie, we asked for a payment to process a
  * payment for a transaction, or we told a payment method about an IPN, or
  * we told a payment method to
  * "finalize_payment_for" (a transaction), or we told a payment method to
  * process a refund. This should handle firing the correct hooks to
  * indicate
  * what exactly happened and updating the transaction appropriately). This
  * could be integrated directly into EE_Transaction upon save, but we want
  * this logic to be separate from 'normal' plain-jane saving and updating
  * of transactions and payments, and to be tied to payment processing.
  * Note: this method DOES NOT save the payment passed into it. It is the responsibility
  * of previous code to decide whether or not to save (because the payment passed into
  * this method might be a temporary, never-to-be-saved payment from an offline gateway,
  * in which case we only want that payment object for some temporary usage during this request,
  * but we don't want it to be saved).
  *
  * @param EE_Transaction|int $transaction
  * @param EE_Payment     $payment
  * @param boolean        $update_txn
  *                        whether or not to call
  *                        EE_Transaction_Processor::
  *                        update_transaction_and_registrations_after_checkout_or_payment()
  *                        (you can save 1 DB query if you know you're going
  *                        to save it later instead)
  * @param bool           $IPN
  *                        if processing IPNs or other similar payment
  *                        related activities that occur in alternate
  *                        requests than the main one that is processing the
  *                        TXN, then set this to true to check whether the
  *                        TXN is locked before updating
  * @throws \EE_Error
  */
 public function update_txn_based_on_payment($transaction, $payment, $update_txn = true, $IPN = false)
 {
     $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful';
     /** @type EE_Transaction $transaction */
     $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
     // can we freely update the TXN at this moment?
     if ($IPN && $transaction->is_locked()) {
         // don't update the transaction at this exact moment
         // because the TXN is active in another request
         EE_Cron_Tasks::schedule_update_transaction_with_payment(time(), $transaction->ID(), $payment->ID());
     } else {
         // verify payment and that it has been saved
         if ($payment instanceof EE_Payment && $payment->ID()) {
             if ($payment->payment_method() instanceof EE_Payment_Method && $payment->payment_method()->type_obj() instanceof EE_PMT_Base) {
                 $payment->payment_method()->type_obj()->update_txn_based_on_payment($payment);
                 // update TXN registrations with payment info
                 $this->process_registration_payments($transaction, $payment);
             }
             $do_action = $payment->just_approved() ? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful' : $do_action;
         } else {
             // send out notifications
             add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
             $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made';
         }
         // if this is an IPN, then we want to know the initial TXN status prior to updating the TXN
         // so that we know whether the status has changed and notifications should be triggered
         if ($IPN) {
             /** @type EE_Transaction_Processor $transaction_processor */
             $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
             $transaction_processor->set_old_txn_status($transaction->status_ID());
         }
         if ($payment->status() !== EEM_Payment::status_id_failed) {
             /** @type EE_Transaction_Payments $transaction_payments */
             $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
             // set new value for total paid
             $transaction_payments->calculate_total_payments_and_update_status($transaction);
             // call EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() ???
             if ($update_txn) {
                 $this->_post_payment_processing($transaction, $payment, $IPN);
             }
         }
         // granular hook for others to use.
         do_action($do_action, $transaction, $payment);
         do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action');
         //global hook for others to use.
         do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment);
     }
 }
 /**
  * _revenue_per_event_report
  * generates Business Report showing total revenue per event.
  *
  * @param string $period  The period (acceptable by PHP Datetime constructor) for which the report is generated.
  * @return int
  */
 private function _revenue_per_event_report($period = '-1 month')
 {
     $report_ID = 'txn-admin-revenue-per-event-report-dv';
     $TXN = EEM_Transaction::instance();
     $results = $TXN->get_revenue_per_event_report($period);
     $results = (array) $results;
     $revenue = array();
     $subtitle = '';
     if ($results) {
         $revenue[] = array(__('Event (only events that have a revenue greater than 1 are shown)', 'event_espresso'), __('Total Revenue', 'event_espresso'));
         foreach ($results as $result) {
             if ($result->revenue > 1) {
                 $event_name = stripslashes(html_entity_decode($result->event_name, ENT_QUOTES, 'UTF-8'));
                 $event_name = wp_trim_words($event_name, 5, '...');
                 $revenue[] = array($event_name, (double) $result->revenue);
             }
         }
         //setup the date range.
         EE_Registry::instance()->load_helper('DTT_Helper');
         $beginning_date = new DateTime('now' . $period, new DateTimeZone(EEH_DTT_Helper::get_timezone()));
         $ending_date = new DateTime('now', new DateTimeZone(EEH_DTT_Helper::get_timezone()));
         $subtitle = sprintf(_x('For the period: %s to %s', 'Used to give date range', 'event_espresso'), $beginning_date->format('Y-m-d'), $ending_date->format('Y-m-d'));
     }
     $report_title = __('Total Revenue per Event');
     $report_params = array('title' => $report_title, 'subtitle' => $subtitle, 'id' => $report_ID, 'revenue' => $revenue, 'noResults' => empty($revenue), 'noTxnMsg' => sprintf(__('%sThere are currently no transaction records in the last month for this report.%s', 'event_espresso'), '<h2>' . $report_title . '</h2><p>', '</p>'));
     wp_localize_script('ee-txn-reports-js', 'txnRevPerEvent', $report_params);
     return $report_ID;
 }
 /**
  * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price name,
  * and the questions associated with the registrations
  * @param int $event_id
  */
 function report_registrations_for_event($event_id = NULL)
 {
     $reg_fields_to_include = array('TXN_ID', 'ATT_ID', 'REG_ID', 'REG_date', 'REG_code', 'REG_count', 'REG_final_price');
     $att_fields_to_include = array('ATT_fname', 'ATT_lname', 'ATT_email', 'ATT_address', 'ATT_address2', 'ATT_city', 'STA_ID', 'CNT_ISO', 'ATT_zip', 'ATT_phone');
     $registrations_csv_ready_array = array();
     $reg_model = EE_Registry::instance()->load_model('Registration');
     $query_params = apply_filters('FHEE__EE_Export__report_registration_for_event', array(array('OR' => array('Transaction.STS_ID' => array('NOT IN', array(EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code)), 'STS_ID' => EEM_Registration::status_id_approved), 'Ticket.TKT_deleted' => array('IN', array(true, false))), 'order_by' => array('Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'), 'force_join' => array('Transaction', 'Ticket', 'Attendee'), 'caps' => EEM_Base::caps_read_admin), $event_id);
     if ($event_id) {
         $query_params[0]['EVT_ID'] = $event_id;
     } else {
         $query_params['force_join'][] = 'Event';
     }
     $registration_rows = $reg_model->get_all_wpdb_results($query_params);
     //get all questions which relate to someone in this group
     $registration_ids = array();
     foreach ($registration_rows as $reg_row) {
         $registration_ids[] = intval($reg_row['Registration.REG_ID']);
     }
     //		EEM_Question::instance()->show_next_x_db_queries();
     $questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(array(array('Answer.REG_ID' => array('IN', $registration_ids))));
     foreach ($registration_rows as $reg_row) {
         if (is_array($reg_row)) {
             $reg_csv_array = array();
             if (!$event_id) {
                 //get the event's name and Id
                 $reg_csv_array[__('Event', 'event_espresso')] = sprintf(__('%1$s (%2$s)', 'event_espresso'), $this->_prepare_value_from_db_for_display(EEM_Event::instance(), 'EVT_name', $reg_row['Event_CPT.post_title']), $reg_row['Event_CPT.ID']);
             }
             $is_primary_reg = $reg_row['Registration.REG_count'] == '1' ? true : false;
             /*@var $reg_row EE_Registration */
             foreach ($reg_fields_to_include as $field_name) {
                 $field = $reg_model->field_settings_for($field_name);
                 if ($field_name == 'REG_final_price') {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row['Registration.REG_final_price'], 'localized_float');
                 } elseif ($field_name == 'REG_count') {
                     $value = sprintf(__('%s of %s', 'event_espresso'), $this->_prepare_value_from_db_for_display($reg_model, 'REG_count', $reg_row['Registration.REG_count']), $this->_prepare_value_from_db_for_display($reg_model, 'REG_group_size', $reg_row['Registration.REG_group_size']));
                 } elseif ($field_name == 'REG_date') {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row['Registration.REG_date'], 'no_html');
                 } else {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row[$field->get_qualified_column()]);
                 }
                 $reg_csv_array[$this->_get_column_name_for_field($field)] = $value;
                 if ($field_name == 'REG_final_price') {
                     //add a column named Currency after the final price
                     $reg_csv_array[__("Currency", "event_espresso")] = EE_Config::instance()->currency->code;
                 }
             }
             //get pretty status
             $stati = EEM_Status::instance()->localized_status(array($reg_row['Registration.STS_ID'] => __('unknown', 'event_espresso'), $reg_row['TransactionTable.STS_ID'] => __('unknown', 'event_espresso')), FALSE, 'sentence');
             $reg_csv_array[__("Registration Status", 'event_espresso')] = $stati[$reg_row['Registration.STS_ID']];
             //get pretty trnasaction status
             $reg_csv_array[__("Transaction Status", 'event_espresso')] = $stati[$reg_row['TransactionTable.STS_ID']];
             $reg_csv_array[__('Transaction Amount Due', 'event_espresso')] = $is_primary_reg ? $this->_prepare_value_from_db_for_display(EEM_Transaction::instance(), 'TXN_total', $reg_row['TransactionTable.TXN_total'], 'localized_float') : '0.00';
             $reg_csv_array[__('Amount Paid', 'event_espresso')] = $is_primary_reg ? $this->_prepare_value_from_db_for_display(EEM_Transaction::instance(), 'TXN_paid', $reg_row['TransactionTable.TXN_paid'], 'localized_float') : '0.00';
             $payment_methods = array();
             $gateway_txn_ids_etc = array();
             $payment_times = array();
             if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
                 $payments_info = EEM_Payment::instance()->get_all_wpdb_results(array(array('TXN_ID' => $reg_row['TransactionTable.TXN_ID'], 'STS_ID' => EEM_Payment::status_id_approved), 'force_join' => array('Payment_Method')), ARRAY_A, 'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time');
                 foreach ($payments_info as $payment_method_and_gateway_txn_id) {
                     $payment_methods[] = isset($payment_method_and_gateway_txn_id['name']) ? $payment_method_and_gateway_txn_id['name'] : __('Unknown', 'event_espresso');
                     $gateway_txn_ids_etc[] = isset($payment_method_and_gateway_txn_id['gateway_txn_id']) ? $payment_method_and_gateway_txn_id['gateway_txn_id'] : '';
                     $payment_times[] = isset($payment_method_and_gateway_txn_id['payment_time']) ? $payment_method_and_gateway_txn_id['payment_time'] : '';
                 }
             }
             $reg_csv_array[__('Payment Date(s)', 'event_espresso')] = implode(',', $payment_times);
             $reg_csv_array[__('Payment Method(s)', 'event_espresso')] = implode(",", $payment_methods);
             $reg_csv_array[__('Gateway Transaction ID(s)', 'event_espresso')] = implode(',', $gateway_txn_ids_etc);
             //get whether or not the user has checked in
             $reg_csv_array[__("Check-Ins", "event_espresso")] = $reg_model->count_related($reg_row['Registration.REG_ID'], 'Checkin');
             //get ticket of registration and its price
             $ticket_model = EE_Registry::instance()->load_model('Ticket');
             if ($reg_row['Ticket.TKT_ID']) {
                 $ticket_name = $this->_prepare_value_from_db_for_display($ticket_model, 'TKT_name', $reg_row['Ticket.TKT_name']);
                 $datetimes_strings = array();
                 foreach (EEM_Datetime::instance()->get_all_wpdb_results(array(array('Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']), 'order_by' => array('DTT_EVT_start' => 'ASC'), 'default_where_conditions' => 'none')) as $datetime) {
                     $datetimes_strings[] = $this->_prepare_value_from_db_for_display(EEM_Datetime::instance(), 'DTT_EVT_start', $datetime['Datetime.DTT_EVT_start']);
                 }
             } else {
                 $ticket_name = __('Unknown', 'event_espresso');
                 $datetimes_strings = array(__('Unknown', 'event_espresso'));
             }
             $reg_csv_array[$ticket_model->field_settings_for('TKT_name')->get_nicename()] = $ticket_name;
             $reg_csv_array[__("Datetimes of Ticket", "event_espresso")] = implode(", ", $datetimes_strings);
             //get datetime(s) of registration
             //add attendee columns
             foreach ($att_fields_to_include as $att_field_name) {
                 $field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
                 if ($reg_row['Attendee_CPT.ID']) {
                     if ($att_field_name == 'STA_ID') {
                         $value = EEM_State::instance()->get_var(array(array('STA_ID' => $reg_row['Attendee_Meta.STA_ID'])), 'STA_name');
                     } elseif ($att_field_name == 'CNT_ISO') {
                         $value = EEM_Country::instance()->get_var(array(array('CNT_ISO' => $reg_row['Attendee_Meta.CNT_ISO'])), 'CNT_name');
                     } else {
                         $value = $this->_prepare_value_from_db_for_display(EEM_Attendee::instance(), $att_field_name, $reg_row[$field_obj->get_qualified_column()]);
                     }
                 } else {
                     $value = '';
                 }
                 $reg_csv_array[$this->_get_column_name_for_field($field_obj)] = $value;
             }
             //make sure each registration has the same questions in the same order
             foreach ($questions_for_these_regs_rows as $question_row) {
                 if (!isset($reg_csv_array[$question_row['Question.QST_admin_label']])) {
                     $reg_csv_array[$question_row['Question.QST_admin_label']] = null;
                 }
             }
             //now fill out the questions THEY answered
             foreach (EEM_Answer::instance()->get_all_wpdb_results(array(array('REG_ID' => $reg_row['Registration.REG_ID']), 'force_join' => array('Question'))) as $answer_row) {
                 /* @var $answer EE_Answer */
                 if ($answer_row['Question.QST_ID']) {
                     $question_label = $this->_prepare_value_from_db_for_display(EEM_Question::instance(), 'QST_admin_label', $answer_row['Question.QST_admin_label']);
                 } else {
                     $question_label = sprintf(__('Question $s', 'event_espresso'), $answer_row['Answer.QST_ID']);
                 }
                 if (isset($answer_row['Question.QST_type']) && $answer_row['Question.QST_type'] == EEM_Question::QST_type_state) {
                     $reg_csv_array[$question_label] = EEM_State::instance()->get_state_name_by_ID($answer_row['Answer.ANS_value']);
                 } else {
                     $reg_csv_array[$question_label] = $this->_prepare_value_from_db_for_display(EEM_Answer::instance(), 'ANS_value', $answer_row['Answer.ANS_value']);
                 }
             }
             $registrations_csv_ready_array[] = apply_filters('FHEE__EE_Export__report_registrations__reg_csv_array', $reg_csv_array, $reg_row);
         }
     }
     //if we couldn't export anything, we want to at least show the column headers
     if (empty($registrations_csv_ready_array)) {
         $reg_csv_array = array();
         $model_and_fields_to_include = array('Registration' => $reg_fields_to_include, 'Attendee' => $att_fields_to_include);
         foreach ($model_and_fields_to_include as $model_name => $field_list) {
             $model = EE_Registry::instance()->load_model($model_name);
             foreach ($field_list as $field_name) {
                 $field = $model->field_settings_for($field_name);
                 $reg_csv_array[$this->_get_column_name_for_field($field)] = null;
                 //$registration->get($field->get_name());
             }
         }
         $registrations_csv_ready_array[] = $reg_csv_array;
     }
     if ($event_id) {
         $event_slug = EEM_Event::instance()->get_var(array(array('EVT_ID' => $event_id)), 'EVT_slug');
         if (!$event_slug) {
             $event_slug = __('unknown', 'event_espresso');
         }
     } else {
         $event_slug = __('all', 'event_espresso');
     }
     $filename = sprintf("registrations-for-%s", $event_slug);
     $handle = $this->EE_CSV->begin_sending_csv($filename);
     $this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
     $this->EE_CSV->end_sending_csv($handle);
 }
 /**
  * Creates a line item for the taxes subtotal and finds all the tax prices
  * and applies taxes to it
  * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
  * @param EE_Transaction $transaction
  * @return EE_Line_Item
  */
 protected static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = NULL)
 {
     $tax_line_item = EE_Line_Item::new_instance(array('LIN_code' => 'taxes', 'LIN_name' => __('Taxes', 'event_espresso'), 'LIN_type' => EEM_Line_Item::type_tax_sub_total));
     if ($transaction) {
         $transaction = EEM_Transaction::instance()->ensure_is_ID($transaction);
         $total_line_item->set_TXN_ID($transaction);
     }
     $total_line_item->add_child_line_item($tax_line_item);
     //and lastly, add the actual taxes
     self::apply_taxes($total_line_item);
     return $tax_line_item;
 }