public function test_new_typical_transaction__ticket_types_3()
 {
     $different_tickets_purchased = 3;
     //there should be a tax in the system by default
     $taxes = EEM_Price::instance()->get_all_prices_that_are_taxes();
     $this->assertEquals(1, count($taxes));
     $taxes_at_top_priority = array_shift($taxes);
     //array shift twice because 2d array
     $tax = array_shift($taxes_at_top_priority);
     //make transaction
     $txn = $this->new_typical_transaction(array('ticket_types' => $different_tickets_purchased));
     //verify everything
     $regs = $txn->registrations();
     $this->assertEquals($different_tickets_purchased, count($regs));
     $sum_of_regs = 0;
     foreach ($regs as $reg) {
         $sum_of_regs += $reg->final_price();
         $reg = array_shift($regs);
         $tkt = $reg->ticket();
         $this->assertEquals($tkt->price() * (100 + $tax->amount()) / 100, $reg->final_price());
         $this->assertTrue($tkt->taxable());
     }
     $this->assertEquals($txn->total(), $sum_of_regs);
     $total_line_item = $txn->total_line_item();
     $this->assertEquals($txn->total(), $total_line_item->total());
     $this->_ensure_txn_on_line_item_and_children($txn, $total_line_item);
     $this->assertNotEmpty($txn->tax_total());
     $this->assertEquals($txn->ID(), $total_line_item->TXN_ID());
 }
 /**
  * This allows setting the $_price property to a new price object if the incoming args for the
  * new price have a prc_id (or set to default if no prc_id).  This optionally will use any args for price type that is included in the incoming arguments.
  *
  * @since 4.3.0
  * @param int $PRC_ID EE_Price ID
  */
 private function _set_new_price($PRC_ID = 0)
 {
     $this->_price = empty($PRC_ID) ? EEM_Price::instance()->get_one_by_ID($PRC_ID) : $this->factory->price->create();
     //fail safe just in case (so we can be sure to have an price).
     if (empty($this->_price)) {
         $this->_price = $this->factory->price->create();
     }
 }
 /**
  * 		This function is a singleton method used to instantiate the EEM_Attendee 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_Price instance
  */
 public static function instance($timezone = NULL)
 {
     // check if instance of EEM_Price already exists
     if (self::$_instance === NULL) {
         // instantiate Price_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);
     // EEM_Price object
     return self::$_instance;
 }
 /**
  * Makes a complete transaction record with all associated data (ie, its line items,
  * registrations, tickets, datetimes, events, attendees, questions, answers, etc).
  *
  * @param array $options {
  *	@type int $ticket_types the number of different ticket types in this transaction. Default 1
  *	@type int $taxable_tickets how many of those ticket types should be taxable. Default EE_INF
  * @return EE_Transaction
  */
 protected function new_typical_transaction($options = array())
 {
     EE_Registry::instance()->load_helper('Line_Item');
     $txn = $this->new_model_obj_with_dependencies('Transaction');
     $total_line_item = EEH_Line_Item::create_total_line_item($txn->ID());
     $total_line_item->save_this_and_descendants_to_txn($txn->ID());
     if (isset($options['ticket_types'])) {
         $ticket_types = $options['ticket_types'];
     } else {
         $ticket_types = 1;
     }
     if (isset($options['taxable_tickets'])) {
         $taxable_tickets = $options['taxable_tickets'];
     } else {
         $taxable_tickets = EE_INF;
     }
     if (isset($options['fixed_ticket_price_modifiers'])) {
         $fixed_ticket_price_modifiers = $options['fixed_ticket_price_modifiers'];
     } else {
         $fixed_ticket_price_modifiers = 1;
     }
     $taxes = EEM_Price::instance()->get_all_prices_that_are_taxes();
     for ($i = 1; $i <= $ticket_types; $i++) {
         $ticket = $this->new_model_obj_with_dependencies('Ticket', array('TKT_price' => $i * 10, 'TKT_taxable' => $taxable_tickets-- > 0 ? true : false));
         $sum_of_sub_prices = 0;
         for ($j = 1; $j <= $fixed_ticket_price_modifiers; $j++) {
             if ($j == $fixed_ticket_price_modifiers) {
                 $price_amount = $ticket->price() - $sum_of_sub_prices;
             } else {
                 $price_amount = $i * 10 / $fixed_ticket_price_modifiers;
             }
             $price = $this->new_model_obj_with_dependencies('Price', array('PRC_amount' => $price_amount, 'PRC_order' => $j));
             $sum_of_sub_prices += $price->amount();
             $ticket->_add_relation_to($price, 'Price');
         }
         $a_datetime = $this->new_model_obj_with_dependencies('Datetime');
         $ticket->_add_relation_to($a_datetime, 'Datetime');
         $this->assertInstanceOf('EE_Line_Item', EEH_Line_Item::add_ticket_purchase($total_line_item, $ticket));
         $reg_final_price = $ticket->price();
         foreach ($taxes as $taxes_at_priority) {
             foreach ($taxes_at_priority as $tax) {
                 $reg_final_price += $reg_final_price * $tax->amount() / 100;
             }
         }
         $this->new_model_obj_with_dependencies('Registration', array('TXN_ID' => $txn->ID(), 'TKT_ID' => $ticket->ID(), 'STS_ID' => EEM_Registration::status_id_approved, 'EVT_ID' => $a_datetime->get('EVT_ID'), 'REG_count' => 1, 'REG_group_size' => 1, 'REG_final_price' => $reg_final_price));
     }
     $txn->set_total($total_line_item->total());
     $txn->save();
     return $txn;
 }
 /**
  * This generates the ticket row for tickets.
  * This same method is used to generate both the actual rows and the js skeleton row (when default ==
  * true)
  *
  * @param int     $tktrow           Represents the row number being generated.
  * @param mixed null|EE_Ticket $ticket           If default then this will be null.
  * @param EE_Datetime[] $ticket_datetimes    Either an array of all datetimes on all tickets indexed by
  *                                           			   each ticket or empty for  default
  * @param EE_Datetime[] $all_dtts                   All Datetimes on the event or empty for default.
  * @param bool   $default          Whether default row being generated or not.
  * @param EE_Ticket[]  $all_tickets      This is an array of all tickets attached to the event (or empty in the
  *                                       		   case of defaults)
  *
  * @return [type] [description]
  */
 protected function _get_ticket_row($tktrow, $ticket, $ticket_datetimes, $all_dtts, $default = FALSE, $all_tickets = array())
 {
     //if $ticket is not an instance of EE_Ticket then force default to true.
     $default = !$ticket instanceof EE_Ticket ? true : false;
     $prices = !empty($ticket) && !$default ? $ticket->get_many_related('Price', array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC'))) : array();
     //if there is only one price (which would be the base price) or NO prices and this ticket is a default ticket, let's just make sure there are no cached default prices on
     //the object.  This is done by not including any query_params.
     if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
         $prices = $ticket->get_many_related('Price');
     }
     // check if we're dealing with a default ticket in which case we don't want any starting_ticket_datetime_row values set (otherwise there won't be any new relationships created for tickets based off of the default ticket).  This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
     $default_dtt = $default || $ticket instanceof EE_Ticket && $ticket->get('TKT_is_default') ? TRUE : FALSE;
     $tkt_dtts = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()]) ? $ticket_datetimes[$ticket->ID()] : array();
     $ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
     $base_price = $default ? NULL : $ticket->base_price();
     $count_price_mods = EEM_Price::instance()->get_all_default_prices(TRUE);
     //breaking out complicated condition for ticket_status
     if ($default) {
         $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
     } else {
         $ticket_status_class = $ticket->is_default() ? ' tkt-status-' . EE_Ticket::onsale : ' tkt-status-' . $ticket->ticket_status();
     }
     //breaking out complicated condition for TKT_taxable
     if ($default) {
         $TKT_taxable = '';
     } else {
         $TKT_taxable = $ticket->get('TKT_taxable') ? ' checked="checked"' : '';
     }
     $template_args = array('tkt_row' => $default ? 'TICKETNUM' : $tktrow, 'TKT_order' => $default ? 'TICKETNUM' : $tktrow, 'tkt_status_class' => $ticket_status_class, 'display_edit_tkt_row' => ' style="display:none;"', 'edit_tkt_expanded' => '', 'edit_tickets_name' => $default ? 'TICKETNAMEATTR' : 'edit_tickets', 'TKT_name' => $default ? '' : $ticket->get('TKT_name'), 'TKT_start_date' => $default ? '' : $ticket->get_date('TKT_start_date', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time']), 'TKT_end_date' => $default ? '' : $ticket->get_date('TKT_end_date', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time']), 'TKT_status' => $default ? EEH_Template::pretty_status(EE_Ticket::onsale, FALSE, 'sentence') : $ticket->is_default() ? EEH_Template::pretty_status(EE_Ticket::onsale, FALSE, 'sentence') : $ticket->ticket_status(TRUE), 'TKT_price' => $default ? '' : EEH_Template::format_currency($ticket->get_ticket_total_with_taxes(), FALSE, FALSE), 'TKT_price_code' => EE_Registry::instance()->CFG->currency->code, 'TKT_price_amount' => $default ? 0 : $ticket_subtotal, 'TKT_qty' => $default ? '' : $ticket->get_pretty('TKT_qty', 'symbol'), 'TKT_qty_for_input' => $default ? '' : $ticket->get_pretty('TKT_qty', 'input'), 'TKT_uses' => $default ? '' : $ticket->get_pretty('TKT_uses', 'input'), 'TKT_min' => $default ? '' : ($ticket->get('TKT_min') === -1 || $ticket->get('TKT_min') === 0 ? '' : $ticket->get('TKT_min')), 'TKT_max' => $default ? '' : $ticket->get_pretty('TKT_max', 'input'), 'TKT_sold' => $default ? 0 : $ticket->tickets_sold('ticket'), 'TKT_registrations' => $default ? 0 : $ticket->count_registrations(array(array('STS_ID' => array('!=', EEM_Registration::status_id_incomplete)))), 'TKT_ID' => $default ? 0 : $ticket->get('TKT_ID'), 'TKT_description' => $default ? '' : $ticket->get('TKT_description'), 'TKT_is_default' => $default ? 0 : $ticket->get('TKT_is_default'), 'TKT_required' => $default ? 0 : $ticket->required(), 'TKT_is_default_selector' => '', 'ticket_price_rows' => '', 'TKT_base_price' => $default || !$base_price instanceof EE_Price ? '' : $base_price->get_pretty('PRC_amount', 'localized_float'), 'TKT_base_price_ID' => $default || !$base_price instanceof EE_Price ? 0 : $base_price->ID(), 'show_price_modifier' => count($prices) > 1 || $default && $count_price_mods > 0 ? '' : ' style="display:none;"', 'show_price_mod_button' => count($prices) > 1 || $default && $count_price_mods > 0 || !$default && $ticket->get('TKT_deleted') ? ' style="display:none;"' : '', 'total_price_rows' => count($prices) > 1 ? count($prices) : 1, 'ticket_datetimes_list' => $default ? '<li class="hidden"></li>' : '', 'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_dtts), 'ticket_datetime_rows' => $default ? '' : implode(',', $tkt_dtts), 'existing_ticket_price_ids' => $default, '', implode(',', array_keys($prices)), 'ticket_template_id' => $default ? 0 : $ticket->get('TTM_ID'), 'TKT_taxable' => $TKT_taxable, 'display_subtotal' => $ticket instanceof EE_Ticket && $ticket->get('TKT_taxable') ? '' : ' style="display:none"', 'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign, 'TKT_subtotal_amount_display' => EEH_Template::format_currency($ticket_subtotal, FALSE, FALSE), 'TKT_subtotal_amount' => $ticket_subtotal, 'tax_rows' => $this->_get_tax_rows($tktrow, $ticket), 'disabled' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? TRUE : FALSE, 'ticket_archive_class' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? ' ticket-archived' : '', 'trash_icon' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? 'ee-lock-icon ' : 'trash-icon dashicons dashicons-post-trash clickable', 'clone_icon' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? '' : 'clone-icon ee-icon ee-icon-clone clickable');
     $template_args['trash_hidden'] = count($all_tickets) === 1 && $template_args['trash_icon'] != 'ee-lock-icon' ? ' style="display:none"' : '';
     //handle rows that should NOT be empty
     if (empty($template_args['TKT_start_date'])) {
         //if empty then the start date will be now.
         $template_args['TKT_start_date'] = date($this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'], current_time('timestamp'));
         $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
     }
     if (empty($template_args['TKT_end_date'])) {
         //get the earliest datetime (if present);
         $earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related('Datetime', array('order_by' => array('DTT_EVT_start' => 'ASC'))) : NULL;
         if (!empty($earliest_dtt)) {
             $template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time']);
         } else {
             //default so let's just use what's been set for the default date-time which is 30 days from now.
             $template_args['TKT_end_date'] = date($this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'], mktime(24, 0, 0, date("m"), date("d") + 29, date("Y")));
         }
         $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
     }
     //generate ticket_datetime items
     if (!$default) {
         $dttrow = 1;
         foreach ($all_dtts as $dtt) {
             $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item($dttrow, $tktrow, $dtt, $ticket, $ticket_datetimes, $default);
             $dttrow++;
         }
     }
     $prcrow = 1;
     foreach ($prices as $price) {
         if ($price->is_base_price()) {
             $prcrow++;
             continue;
         }
         $show_trash = count($prices) > 1 && $prcrow === 1 || count($prices) === 1 ? FALSE : TRUE;
         $show_create = count($prices) > 1 && count($prices) !== $prcrow ? FALSE : TRUE;
         $template_args['ticket_price_rows'] .= $this->_get_ticket_price_row($tktrow, $prcrow, $price, $default, $ticket, $show_trash, $show_create);
         $prcrow++;
     }
     //filter $template_args
     $template_args = apply_filters('FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args', $template_args, $tktrow, $ticket, $ticket_datetimes, $all_dtts, $default, $all_tickets, $this->_is_creating_event);
     $template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php';
     return EEH_Template::display_template($template, $template_args, TRUE);
 }
 /**
  * Tests that if we are joining to a table that has no entries matching the query,
  * but the primary table does that we don't create model objects for the non-existent
  * row, but we do still create model objects for the row that did exist.
  * @group 7634
  */
 function test_get_all_if_related_model_blank_use_nulls()
 {
     $price_sans_price_type = EE_Price::new_instance(array('PRC_name' => 'original', 'PRT_ID' => EEM_Price_Type::instance()->count() + 1));
     $price_sans_price_type->save();
     $fetched_price = EEM_Price::reset()->get_one(array(array('PRC_ID' => $price_sans_price_type->ID()), 'force_join' => array('Price_Type')));
     $this->assertInstanceOf('EE_Price', $fetched_price);
     $this->assertEquals(null, $fetched_price->type_obj());
 }
 /**
  * return the price object for a given price ID.
  *
  * @since 4.3.0
  *
  * @param int $PRC_ID the price id for the price to attempt to retrieve
  *
  * @return mixed null|EE_Price
  */
 public function get_object_by_id($PRC_ID)
 {
     return EEM_Price::instance()->get_one_by_ID($PRC_ID);
 }
 public function update_price_order()
 {
     $success = __('Price order was updated successfully.', 'event_espresso');
     // grab our row IDs
     $row_ids = isset($this->_req_data['row_ids']) && !empty($this->_req_data['row_ids']) ? explode(',', rtrim($this->_req_data['row_ids'], ',')) : FALSE;
     if (is_array($row_ids)) {
         for ($i = 0; $i < count($row_ids); $i++) {
             //Update the prices when re-ordering
             $id = absint($row_ids[$i]);
             if (EEM_Price::instance()->update(array('PRC_order' => $i + 1), array(array('PRC_ID' => $id))) === FALSE) {
                 $success = FALSE;
             }
         }
     } else {
         $success = FALSE;
     }
     $errors = !$success ? __('An error occurred. The price order was not updated.', 'event_espresso') : FALSE;
     echo json_encode(array('return_data' => FALSE, 'success' => $success, 'errors' => $errors));
     die;
 }
 /**
  * Makes a complete transaction record with all associated data (ie, its line items,
  * registrations, tickets, datetimes, events, attendees, questions, answers, etc).
  *
  * @param array $options {
  *	@type int $ticket_types the number of different ticket types in this transaction. Deafult 1
  *	@type int $taxable_tickets how many of those ticket types should be taxable. Default INF
  * @return EE_Transaction
  */
 protected function new_typical_transaction($options = array())
 {
     EE_Registry::instance()->load_helper('Line_Item');
     $txn = $this->new_model_obj_with_dependencies('Transaction');
     $total_line_item = EEH_Line_Item::create_default_total_line_item($txn->ID());
     $total_line_item->save_this_and_descendants_to_txn($txn->ID());
     if (isset($options['ticket_types'])) {
         $ticket_types = $options['ticket_types'];
     } else {
         $ticket_types = 1;
     }
     if (isset($options['taxable_tickets'])) {
         $taxable_tickets = $options['taxable_tickets'];
     } else {
         $taxable_tickets = INF;
     }
     $taxes = EEM_Price::instance()->get_all_prices_that_are_taxes();
     for ($i = 1; $i <= $ticket_types; $i++) {
         $ticket = $this->new_model_obj_with_dependencies('Ticket', array('TKT_price' => $i * 10, 'TKT_taxable' => $taxable_tickets--));
         $this->assertInstanceOf('EE_Line_Item', EEH_Line_Item::add_ticket_purchase($total_line_item, $ticket));
         $reg_final_price = $ticket->price();
         foreach ($taxes as $taxes_at_priority) {
             foreach ($taxes_at_priority as $tax) {
                 $reg_final_price += $reg_final_price * $tax->amount() / 100;
             }
         }
         $this->new_model_obj_with_dependencies('Registration', array('TXN_ID' => $txn->ID(), 'TKT_ID' => $ticket->ID(), 'REG_count' => 1, 'REG_group_size' => 1, 'REG_final_price' => $reg_final_price));
     }
     $txn->set_total($total_line_item->total());
     $txn->save();
     return $txn;
 }
 /**
  * get EE_Price_Type ID.
  *
  * @since 1.0.0
  *
  * @return int
  */
 public function price_type_id()
 {
     $price = EEM_Price::instance()->get_one_by_ID($this->price_ID());
     return $price instanceof EE_Price ? $price->type() : 0;
 }
 /**
  * Alters the registration csv report generated from the normal registration list table.
  * Add a column
  * @param array $csv_row
  * @param array $reg_db_row
  * @return array
  */
 public static function add_promotions_column_to_reg_csv_report($csv_row, $reg_db_row)
 {
     $promo_rows = EEM_Price::instance()->get_all_wpdb_results(array(array('Promotion.Line_Item.TXN_ID' => $reg_db_row['Registration.TXN_ID'])));
     $promos_for_csv_col = array();
     foreach ($promo_rows as $promo_row) {
         if ($promo_row['Promotion.PRO_code']) {
             $promos_for_csv_col[] = sprintf('%1$s [%2$s]', $promo_row['Price.PRC_name'], $promo_row['Promotion.PRO_code']);
         } else {
             $promos_for_csv_col[] = $promo_row['Price.PRC_name'];
         }
     }
     $csv_row[__('Transaction Promotions', 'event_espresso')] = implode(',', $promos_for_csv_col);
     return $csv_row;
 }