/**
  * test_add_ticket_purchase
  */
 public function test_add_ticket_purchase()
 {
     // create grand total
     $total_line_item = EEH_Line_Item::create_default_total_line_item();
     $this->assertEquals(0, $total_line_item->total());
     // create a ticket
     $ticket = $this->new_ticket(array('dollar_surcharge' => 5, 'percent_surcharge' => 10, 'datetimes' => 2));
     // need to save ticket for other tests to work
     $ticket->save();
     // two tickets plz
     $ticket_line_item = EEH_Line_Item::add_ticket_purchase($total_line_item, $ticket, 2);
     // confirm totals
     $this->assertEquals(2, $ticket_line_item->quantity());
     $this->assertEquals(33, $ticket_line_item->total());
     $this->assertEquals(4.95, EEH_Line_Item::get_taxes_subtotal($total_line_item)->total());
     $this->assertEquals(33, EEH_Line_Item::get_items_subtotal($total_line_item)->total());
     $this->assertEquals(37.95, $total_line_item->total());
     $this->assertNotEquals(0, $total_line_item->total());
     // one moar ticket plz
     $ticket_line_item = EEH_Line_Item::add_ticket_purchase($total_line_item, $ticket);
     // confirm totals
     $this->assertEquals(3, $ticket_line_item->quantity());
     $this->assertEquals(49.5, $ticket_line_item->total());
     $this->assertEquals(7.43, EEH_Line_Item::get_taxes_subtotal($total_line_item)->total());
     $this->assertEquals(49.5, EEH_Line_Item::get_items_subtotal($total_line_item)->total());
     $this->assertEquals(56.93, $total_line_item->total());
     $this->assertNotEquals(0, $total_line_item->total());
     // total ticket line items count? should just be one ticket line item
     $this->assertEquals(1, count($total_line_item->get_child_line_item('tickets')->children()));
     // now add a different ticket
     $new_ticket = $this->new_ticket(array('ticket_price' => 10, 'ticket_taxable' => false, 'datetimes' => 1));
     $new_ticket->save();
     // add one
     $new_ticket_line_item = EEH_Line_Item::add_ticket_purchase($total_line_item, $new_ticket);
     $this->assertEquals(1, $new_ticket_line_item->quantity());
     // add one moar
     $new_ticket_line_item = EEH_Line_Item::add_ticket_purchase($total_line_item, $new_ticket);
     $this->assertEquals(2, $new_ticket_line_item->quantity());
     // confirm totals
     $this->assertEquals(20, $new_ticket_line_item->total());
     // should be same taxes as before
     $this->assertEquals(7.43, EEH_Line_Item::get_taxes_subtotal($total_line_item)->total());
     $this->assertEquals(69.5, EEH_Line_Item::get_items_subtotal($total_line_item)->total());
     $this->assertEquals(76.93000000000001, $total_line_item->total());
     $this->assertNotEquals(0, $total_line_item->total());
     // total ticket ticket line items?
     $this->assertEquals(2, count($total_line_item->get_child_line_item('tickets')->children()));
 }
 /**
  * @group 8964
  * Uses a particular number and quantity that has been shown to cause rounding problems
  * prior to the work on 8964 (specifically, if you had 2 transactions for 1 ticket purchase each
  * the total for both transactions was NOT the same as 1 transaction for 2 ticket purchases)
  */
 function test_recalculate_pre_tax_total__rounding_issues()
 {
     EE_Registry::instance()->load_helper('Line_Item');
     $flat_base_price_type_id = EEM_Price_Type::instance()->get_var(array(array('PRT_name' => 'Base Price')));
     $percent_surcharge_price_type_id = EEM_Price_Type::instance()->get_var(array(array('PRT_name' => 'Percent Surcharge')));
     $base_price = $this->new_model_obj_with_dependencies('Price', array('PRT_ID' => $flat_base_price_type_id, 'PRC_amount' => 21.67));
     $percent_surcharge = $this->new_model_obj_with_dependencies('Price', array('PRT_ID' => $percent_surcharge_price_type_id, 'PRC_amount' => 20));
     $ticket = $this->new_model_obj_with_dependencies('Ticket', array('TKT_price' => $base_price->amount() + $base_price->amount() * $percent_surcharge->amount() / 100, 'TKT_taxable' => false));
     $ticket->_add_relation_to($base_price, 'Price');
     $ticket->_add_relation_to($percent_surcharge, 'Price');
     $event = $this->new_model_obj_with_dependencies('Event');
     $datetime = $this->new_model_obj_with_dependencies('Datetime');
     $ticket->_add_relation_to($datetime, 'Datetime');
     $quantity = 2;
     $total_line_item = EEH_Line_Item::add_ticket_purchase(EEH_Line_Item::create_total_line_item(), $ticket, $quantity);
     $this->assertEquals($ticket->price() * $quantity, $total_line_item->total());
 }
 /**
  * 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;
 }
 protected function _setup_data()
 {
     //need to figure out the running total for test purposes so... we're going to create a temp cart and add the tickets to it!
     EE_Registry::instance()->SSN->clear_session(__CLASS__, __FUNCTION__);
     $cart = EE_Cart::reset();
     //add tickets to cart
     foreach ($this->tickets as $ticket) {
         $cart->add_ticket_to_cart($ticket['ticket']);
     }
     //setup txn property
     $this->txn = EE_Transaction::new_instance(array('TXN_timestamp' => time(), 'TXN_total' => 0, 'TXN_paid' => 0, 'STS_ID' => EEM_Transaction::incomplete_status_code, 'TXN_session_data' => NULL, 'TXN_hash_salt' => NULL, 'TXN_ID' => 999999));
     //setup reg_objects
     //note we're setting up a reg object for each attendee in each event but ALSO adding to the reg_object array.
     $this->reg_objs = array();
     $regid = 9999990;
     foreach ($this->_attendees as $key => $attendee) {
         //note we need to setup reg_objects for each event this attendee belongs to
         $regatt = $attendee['att_obj']->ID();
         $regtxn = $this->txn->ID();
         $regcnt = 1;
         foreach ($attendee['line_ref'] as $evtid) {
             foreach ($this->_events[$evtid]['tkt_objs'] as $ticket) {
                 $reg_array = array('EVT_ID' => $evtid, 'ATT_ID' => $regatt, 'TXN_ID' => $regtxn, 'TKT_ID' => $ticket->ID(), 'STS_ID' => EEM_Registration::status_id_pending_payment, 'REG_date' => time(), 'REG_final_price' => $ticket->get('TKT_price'), 'REG_session' => 'dummy_session_id', 'REG_code' => $regid . '-dummy-generated-code', 'REG_url_link' => $regcnt . '-daafpapasdlfakasdfpqasdfasdf', 'REG_count' => $regcnt, 'REG_group_size' => $this->_events[$evtid]['total_attendees'], 'REG_att_is_going' => TRUE, 'REG_ID' => $regid);
                 $REG_OBJ = EE_Registration::new_instance($reg_array);
                 $this->_attendees[$key]['reg_objs'][$regid] = $REG_OBJ;
                 $this->_events[$evtid]['reg_objs'][] = $REG_OBJ;
                 $this->reg_objs[] = $REG_OBJ;
                 $this->tickets[$ticket->ID()]['reg_objs'][$regid] = $REG_OBJ;
                 $regcnt++;
                 $regid++;
             }
         }
     }
     //setup line items!
     EE_Registry::instance()->load_helper('Line_Item');
     $line_item_total = EEH_Line_Item::create_total_line_item($this->txn);
     //add tickets
     foreach ($this->tickets as $tktid => $item) {
         $qty = $item['count'];
         $ticket = $item['ticket'];
         EEH_Line_Item::add_ticket_purchase($line_item_total, $ticket, $qty);
     }
     $shipping_line_item = EE_Line_Item::new_instance(array('LIN_name' => __('Shipping Surcharge', 'event_espresso'), 'LIN_desc' => __('Sent via Millenium Falcon', 'event_espresso'), 'LIN_unit_price' => 20, 'LIN_quantity' => 1, 'LIN_is_taxable' => TRUE, 'LIN_total' => 20, 'LIN_type' => EEM_Line_Item::type_line_item));
     EEH_Line_Item::add_item($line_item_total, $shipping_line_item);
     $this->additional_line_items = array($shipping_line_item);
     //now let's add taxes
     EEH_Line_Item::apply_taxes($line_item_total);
     //now we should be able to get the items we need from this object
     $event_line_items = EEH_Line_Item::get_pre_tax_subtotal($line_item_total)->children();
     $line_items = array();
     foreach ($event_line_items as $line_id => $line_item) {
         if (!$line_item instanceof EE_Line_Item || $line_item->OBJ_type() !== 'Event') {
             continue;
         }
         $ticket_line_items = EEH_Line_Item::get_ticket_line_items($line_item);
         foreach ($ticket_line_items as $ticket_line_id => $ticket_line_item) {
             if (!$ticket_line_item instanceof EE_Line_Item) {
                 continue;
             }
             $this->tickets[$ticket_line_item->OBJ_ID()]['line_item'] = $ticket_line_item;
             $this->tickets[$ticket_line_item->OBJ_ID()]['sub_line_items'] = $ticket_line_item->children();
             $line_items[$ticket_line_item->ID()]['children'] = $ticket_line_item->children();
             $line_items[$ticket_line_item->ID()]['EE_Ticket'] = $this->tickets[$ticket_line_item->OBJ_ID()]['ticket'];
         }
     }
     $this->line_items_with_children = $line_items;
     $this->tax_line_items = $line_item_total->tax_descendants();
     //add proper total to transaction object.
     $grand_total = $line_item_total->recalculate_total_including_taxes();
     $this->grand_total_line_item = $line_item_total;
     $this->txn->set_total($grand_total);
     //add additional details for each registration
     foreach ($this->reg_objs as $reg) {
         $this->_registrations[$reg->ID()]['tkt_obj'] = $this->tickets[$reg->get('TKT_ID')]['ticket'];
         $this->_registrations[$reg->ID()]['evt_obj'] = $this->_events[$reg->get('EVT_ID')]['event'];
         $this->_registrations[$reg->ID()]['reg_obj'] = $reg;
         $this->_registrations[$reg->ID()]['ans_objs'] = $this->_attendees[$reg->get('ATT_ID')]['ans_objs'];
         $this->_registrations[$reg->ID()]['att_obj'] = $this->_attendees[$reg->get('ATT_ID')]['att_obj'];
         $this->_registrations[$reg->ID()]['dtt_objs'] = $this->tickets[$reg->get('TKT_ID')]['dtt_objs'];
     }
     //events and attendees
     $this->events = $this->_events;
     $this->attendees = $this->_attendees;
     $this->registrations = $this->_registrations;
     $attendees_to_shift = $this->_attendees;
     //setup primary attendee property
     $this->primary_attendee_data = array('fname' => $this->_attendees[999999991]['att_obj']->fname(), 'lname' => $this->_attendees[999999991]['att_obj']->lname(), 'email' => $this->_attendees[999999991]['att_obj']->email(), 'att_obj' => $this->_attendees[999999991]['att_obj'], 'reg_obj' => array_shift($attendees_to_shift[999999991]['reg_objs']));
     //reg_info property
     //note this isn't referenced by any shortcode parsers so we'll ignore for now.
     $this->reg_info = array();
     //let's set a reg_obj for messengers expecting one.
     $this->reg_obj = array_pop($this->_attendees[999999991]['reg_objs']);
     //the below are just dummy items.
     $this->user_id = 1;
     $this->ip_address = '192.0.2.1';
     $this->user_agent = '';
     $this->init_access = time();
     $this->last_access = time();
 }
 /**
  *	@process items for adding to cart
  *	@access public
  *	@param EE_Ticket $ticket
  *	@param int $qty
  *	@return TRUE on success, FALSE on fail
  */
 public function add_ticket_to_cart(EE_Ticket $ticket, $qty = 1)
 {
     EEH_Line_Item::add_ticket_purchase($this->_grand_total, $ticket, $qty);
     return $this->save_cart() ? TRUE : FALSE;
 }
 /**
  * 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;
 }
 /**
  * @group 8193
  */
 public function test_calculate_reg_final_prices_per_line_item__3_taxable_tickets_with_a_discount()
 {
     $transaction = $this->new_typical_transaction(array('ticket_types' => 2));
     //add another ticket purchase for one of the same events
     $event1 = EEM_Event::instance()->get_one(array(array('Registration.TXN_ID' => $transaction->ID())));
     $dtt = $event1->get_first_related('Datetime');
     $new_tkt = $this->new_model_obj_with_dependencies('Ticket', array('TKT_price' => 6));
     $new_tkt->_add_relation_to($dtt, 'Datetime');
     $quantity_of_new_tks_purchased = 2;
     EEH_Line_Item::add_ticket_purchase($transaction->total_line_item(), $new_tkt, $quantity_of_new_tks_purchased);
     for ($i = 0; $i < 2; $i++) {
         $this->new_model_obj_with_dependencies('Registration', array('TXN_ID' => $transaction->ID(), 'EVT_ID' => $dtt->get('EVT_ID'), 'TKT_ID' => $new_tkt->ID()));
     }
     //and add an unrelated purchase
     EEH_Line_Item::add_unrelated_item($transaction->total_line_item(), 'Transaction-Wide Discount', -5);
     $totals = EEH_Line_Item::calculate_reg_final_prices_per_line_item($transaction->total_line_item());
     //		honestly the easiest way to confirm the total was right is to visualize the tree
     //		var_dump( $totals );
     //		EEH_Line_Item::visualize( $transaction->total_line_item() );
     //verify that if we added the REG_final_prices onto the regs as derived from $totals,
     //that it would equal the grand total
     $sum_of_regs_final_prices = 0;
     foreach ($transaction->registrations() as $reg) {
         $ticket_line_item = EEM_Line_Item::instance()->get_line_item_for_registration($reg);
         $sum_of_regs_final_prices += $totals[$ticket_line_item->ID()];
     }
     $this->assertEquals($totals['total'], $sum_of_regs_final_prices);
     //ok now let's verify the 'REG_final_price' for each ticket's line item is what we expect it to be
     //so there should be 3 ticket line items right?
     $ticket_line_items = EEM_Line_Item::instance()->get_all(array(array('TXN_ID' => $transaction->ID(), 'OBJ_type' => 'Ticket')));
     //one ticket should be 10 pre-tax
     $ten_dollar_ticket = EEM_Line_Item::instance()->get_one(array(array('TXN_ID' => $transaction->ID(), 'LIN_unit_price' => 10, 'LIN_type' => EEM_Line_Item::type_line_item)));
     $this->assertEquals(10.31, round($totals[$ten_dollar_ticket->ID()], 2));
     //one ticket should be 20 pre-tax
     $twenty_dollar_ticket = EEM_Line_Item::instance()->get_one(array(array('TXN_ID' => $transaction->ID(), 'LIN_unit_price' => 20, 'LIN_type' => EEM_Line_Item::type_line_item)));
     $this->assertEquals(20.62, round($totals[$twenty_dollar_ticket->ID()], 2));
     //one ticket should be for 6 pre-tax (although its non-taxable anyway)
     $six_dollar_ticket = EEM_Line_Item::instance()->get_one(array(array('TXN_ID' => $transaction->ID(), 'LIN_unit_price' => 6, 'LIN_type' => EEM_Line_Item::type_line_item)));
     $this->assertEquals(5.29, round($totals[$six_dollar_ticket->ID()], 2));
 }