/**
  * like test_REG_final_price_matches_total_of_filtering_line_item_tree,
  * but makes sure the tickets have sub-prices, because that has shown to have some
  * bugs with calculations so far
  */
 function test_REG_final_price_matches_total_of_filtering_line_item_tree__with_sub_line_items()
 {
     $transaction = $this->new_typical_transaction(array('ticket_types' => 2, 'fixed_ticket_price_modifiers' => 2));
     //add another ticket purchase for one of the same events
     $event1 = EEM_Event::instance()->get_one(array(array('Registration.TXN_ID' => $transaction->ID())));
     $event_line_item = EEM_Line_Item::instance()->get_one(array(array('TXN_ID' => $transaction->ID(), 'OBJ_type' => 'Event', 'OBJ_ID' => $event1->ID())));
     $discount = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_type' => EEM_Line_Item::type_line_item, 'LIN_name' => 'event discount', 'LIN_total' => -8, 'LIN_unit_price' => -8, 'LIN_percent' => 0, 'LIN_quantity' => 1, 'LIN_parent' => $event_line_item->ID(), 'LIN_percent' => null, 'LIN_order' => count($event_line_item->children())));
     $transaction->total_line_item()->recalculate_pre_tax_total();
     //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() );
     //for each registration on the tranasction, verify the REG_final_price
     //indicated by EEH_Line_Item::calculate_reg_final_prices_per_line_item matches
     //what the line item filters would have returned
     EEH_Autoloader::register_line_item_filter_autoloaders();
     foreach ($transaction->registrations() as $registration) {
         $ticket_line_item = EEM_Line_Item::instance()->get_line_item_for_registration($registration);
         $reg_final_price_from_line_item_helper = $totals[$ticket_line_item->ID()];
         //now get the line item filter's final price
         $filters = new EE_Line_Item_Filter_Collection();
         $filters->add(new EE_Single_Registration_Line_Item_Filter($registration));
         $line_item_filter_processor = new EE_Line_Item_Filter_Processor($filters, $transaction->total_line_item());
         $filtered_line_item_tree = $line_item_filter_processor->process();
         $reg_final_price_from_line_item_filter = $filtered_line_item_tree->total();
         $this->assertLessThan(0.2, abs($reg_final_price_from_line_item_filter - $reg_final_price_from_line_item_helper));
     }
 }
 function test_get_nearest_descendant_of_type()
 {
     $txn = $this->new_typical_transaction();
     $line_item = $txn->total_line_item();
     $old_tax_subtotal = EEH_Line_Item::get_nearest_descendant_of_type($line_item, EEM_Line_Item::type_tax_sub_total);
     $this->assertInstanceOf('EE_Line_Item', $old_tax_subtotal);
     $this->assertEquals(EEM_Line_Item::type_tax_sub_total, $old_tax_subtotal->type());
     $old_tax = EEH_Line_Item::get_nearest_descendant_of_type($line_item, EEM_Line_Item::type_tax);
     $this->assertInstanceOf('EE_Line_Item', $old_tax_subtotal);
     $this->assertEquals(EEM_Line_Item::type_tax, $old_tax->type());
 }
 /**
  * @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());
 }
 /**
  * @save cart to session
  * @access public
  * @param bool $apply_taxes
  * @return TRUE on success, FALSE on fail
  */
 public function save_cart($apply_taxes = TRUE)
 {
     if ($apply_taxes && $this->_grand_total instanceof EE_Line_Item) {
         EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
         //make sure we don't cache the transaction because it can get stale
         if ($this->_grand_total->get_one_from_cache('Transaction') instanceof EE_Transaction && $this->_grand_total->get_one_from_cache('Transaction')->ID()) {
             $this->_grand_total->clear_cache('Transaction', null, true);
         }
     }
     if ($this->_session instanceof EE_Session) {
         return $this->_session->set_cart($this);
     } else {
         return 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;
 }
 /**
  * generate_promotion_line_items
  * if the promotion in question has not already been redeemed for the given line item,
  * then have a line item generated by the promotion scope object, and increment
  *
  * @since    1.0.4
  * @access 	public
  * @param 	\EE_Cart $cart
  * @return 	\EE_Event[]
  */
 public function get_events_from_cart(EE_Cart $cart)
 {
     $event_line_items = $object_type_line_items = EEH_Line_Item::get_event_subtotals($cart->get_grand_total());
     $events = array();
     foreach ($event_line_items as $event_line_item) {
         if ($event_line_item instanceof EE_Line_Item) {
             $events[$event_line_item->OBJ_ID()] = $event_line_item->get_object();
         }
     }
     return $events;
 }
 /**
  * Gets the tax subtotal line item (assumes there's only one)
  * @return EE_Line_Item
  */
 public function tax_total_line_item()
 {
     return EEH_Line_Item::get_taxes_subtotal($this->total_line_item());
 }
 /**
  * Updates the registration' final prices based on the current line item tree (taking into account
  * discounts, taxes, and other line items unrelated to tickets.)
  * @param EE_Transaction $transaction
  * @param boolean $save_regs whether to immediately save registrations in this function or not
  * @return void
  */
 public function update_registration_final_prices($transaction, $save_regs = true)
 {
     $reg_final_price_per_ticket_line_item = EEH_Line_Item::calculate_reg_final_prices_per_line_item($transaction->total_line_item());
     foreach ($transaction->registrations() as $registration) {
         $line_item = EEM_Line_Item::instance()->get_line_item_for_registration($registration);
         if (isset($reg_final_price_per_ticket_line_item[$line_item->ID()])) {
             $registration->set_final_price($reg_final_price_per_ticket_line_item[$line_item->ID()]);
             if ($save_regs) {
                 $registration->save();
             }
         }
     }
     //and make sure there's no rounding problem
     $this->fix_reg_final_price_rounding_issue($transaction);
 }
 /**
  * get_object_line_items_from_cart
  * searches the line items for any objects that this promotion applies to
  *
  * @since   1.0.0
  *
  * @param EE_Line_Item  $total_line_item the EE_Cart grand total line item to be searched
  * @param array         $redeemable_scope_promos
  * @param string        $OBJ_type
  * @return \EE_Line_Item[]
  */
 public function get_object_line_items_from_cart(EE_Line_Item $total_line_item, $redeemable_scope_promos = array(), $OBJ_type = '')
 {
     EE_Registry::instance()->load_helper('Line_Item');
     $applicable_items = array();
     $OBJ_type = empty($OBJ_type) ? $this->slug : $OBJ_type;
     // check that redeemable scope promos for the requested type exist
     if (isset($redeemable_scope_promos[$OBJ_type])) {
         $object_type_line_items = EEH_Line_Item::get_line_items_by_object_type_and_IDs($total_line_item, $OBJ_type, $redeemable_scope_promos[$OBJ_type]);
         if (is_array($object_type_line_items)) {
             foreach ($object_type_line_items as $object_type_line_item) {
                 if (apply_filters('FHEE__EE_Promotion_Scope__get_object_line_items_from_cart__is_applicable_item', true, $object_type_line_item)) {
                     $applicable_items[] = $object_type_line_item;
                 }
             }
         }
     }
     return $applicable_items;
 }
 /**
  * maybe_delete_event_line_item
  * checks if an event line item still has any tickets associated with it,and
  * and if not, then deletes the event plus any other non-ticket items,
  * which may be things like promotion codes
  *
  * @access public
  * @param \EE_Line_Item $parent_line_item
  * @return void
  */
 public function maybe_delete_event_line_item($parent_line_item = null)
 {
     if (!$parent_line_item instanceof EE_Line_Item) {
         return;
     }
     // are there any tickets left for this event ?
     $ticket_line_items = EEH_Line_Item::get_ticket_line_items($parent_line_item);
     if (empty($ticket_line_items)) {
         // find and delete ALL children which may include non-ticket items like promotions
         $child_line_items = $parent_line_item->children();
         if (!empty($child_line_items)) {
             foreach ($child_line_items as $child_line_item) {
                 $this->_delete_line_item($child_line_item);
             }
         }
         $this->_delete_line_item($parent_line_item);
     }
 }
 /**
  * Verifies that we don't re-add shipping if it's already been added
  * @group 4710
  */
 public function test_set_redirect_info__with_promotion()
 {
     $ppm = $this->new_model_obj_with_dependencies('Payment_Method', array('PMD_type' => 'Paypal_Standard'));
     $ppg = $ppm->type_obj()->get_gateway();
     $ppg->set_settings($this->_test_settings);
     $t = $this->new_typical_transaction(array('ticket_types' => 2));
     $event = EEM_Event::instance()->get_one(array(array('Registration.TXN_ID' => $t->ID())));
     $event_li = EEH_Line_Item::get_event_line_item($t->total_line_item(), $event);
     $discount_li = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_parent' => $event_li->ID(), 'LIN_name' => 'discount', 'LIN_code' => 'discount', 'LIN_unit_price' => -10, 'LIN_quantity' => 1, 'LIN_percent' => 0, 'LIN_type' => EEM_Line_Item::type_line_item, 'LIN_is_taxable' => false, 'TXN_ID' => $t->ID()));
     $t->total_line_item()->recalculate_total_including_taxes();
     $p = $this->new_model_obj_with_dependencies('Payment', array('TXN_ID' => $t->ID(), 'PMD_ID' => $ppm->ID(), 'PAY_amount' => $t->total()));
     $this->assertNotEquals(EEM_Payment::status_id_approved, $p->status());
     $p = $ppg->set_redirection_info($p, NULL, self::return_url, self::notify_url, self::cancel_url);
     $rargs = $p->redirect_args();
     //also check we DID enumerat ethe line items
     $this->assertEquals('10', $rargs['discount_amount_cart']);
     $this->assertTrue(isset($rargs['item_name_1']));
     $this->assertTrue(isset($rargs['amount_1']));
     //although we shouldn't be mentioning how much taxes are per item. leave that to paypal
     $this->assertFalse(isset($rargs['tax_1']));
     $this->assertTrue(isset($rargs['item_name_2']));
     $this->assertTrue(isset($rargs['amount_2']));
     $this->assertTrue(isset($rargs['quantity_2']));
     $this->assertFalse(isset($rargs['tax_2']));
 }
 /**
  * Verifies discounts only apply to the their sibling ticket line item's REG_final_prices
  * @group 8541
  */
 function test_calculate_reg_final_prices_per_line_item__discount_only_for_one_event_subtotal()
 {
     $grand_total = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_name' => 'total', 'LIN_type' => EEM_Line_Item::type_total, 'LIN_total' => 0));
     $subtotal_a = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_name' => 'subtotal_a', 'LIN_type' => EEM_Line_Item::type_sub_total, 'LIN_total' => 0, 'LIN_unit_price' => 0, 'LIN_quantity' => 0, 'LIN_parent' => $grand_total->ID(), 'LIN_order' => 0));
     $subtotal_b = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_name' => 'subtotal_b', 'LIN_type' => EEM_Line_Item::type_sub_total, 'LIN_total' => 0, 'LIN_unit_price' => 0, 'LIN_quantity' => 0, 'LIN_parent' => $grand_total->ID(), 'LIN_order' => 1));
     $ticket_line_item_a = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_name' => 'ticket_line_item_a', 'LIN_type' => EEM_Line_Item::type_line_item, 'LIN_is_taxable' => false, 'LIN_total' => 10, 'LIN_unit_price' => 10, 'LIN_quantity' => 1, 'LIN_parent' => $subtotal_a->ID(), 'LIN_order' => 1, 'OBJ_type' => 'Ticket'));
     $ticket_line_item_b = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_name' => 'ticket_line_item_b', 'LIN_type' => EEM_Line_Item::type_line_item, 'LIN_is_taxable' => false, 'LIN_total' => 10, 'LIN_unit_price' => 10, 'LIN_quantity' => 1, 'LIN_parent' => $subtotal_b->ID(), 'LIN_order' => 1, 'OBJ_type' => 'Ticket'));
     $discount_for_b = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_name' => 'discount_for_b', 'LIN_type' => EEM_Line_Item::type_line_item, 'LIN_is_taxable' => false, 'LIN_total' => -5, 'LIN_unit_price' => 0, 'LIN_percent' => -50, 'LIN_quantity' => 1, 'LIN_parent' => $subtotal_b->ID(), 'LIN_order' => 100));
     $taxes_subtotal = $this->new_model_obj_with_dependencies('Line_Item', array('LIN_name' => 'taxes', 'LIN_type' => EEM_Line_Item::type_tax_sub_total, 'LIN_percent' => 0, 'LIN_parent' => $grand_total->ID(), 'LIN_order' => 1));
     $totals = EEH_Line_Item::calculate_reg_final_prices_per_line_item($grand_total);
     //		var_dump($totals);
     //		EEH_Line_Item::visualize( $grand_total );
     //now verify the discount only applied to event B's ticket, not event A's
     $this->assertEquals(10, $totals[$ticket_line_item_a->ID()]);
     $this->assertEquals(5, $totals[$ticket_line_item_b->ID()]);
 }
 /**
  *	@save cart to session
  *	@access public
  *	@return TRUE on success, FALSE on fail
  */
 public function save_cart()
 {
     EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
     //make sure we don't cache the transaction because it can get stale
     if ($this->_grand_total->get_one_from_cache('Transaction') instanceof EE_Transaction && $this->_grand_total->get_one_from_cache('Transaction')->ID()) {
         $this->_grand_total->clear_cache('Transaction', null, true);
     }
     return EE_Registry::instance()->SSN->set_cart($this);
 }
 public function test_handle_payment_update__paypal_adds_taxes_and_shipping()
 {
     $ppm = $this->new_model_obj_with_dependencies('Payment_Method', array('PMD_type' => 'Paypal_Standard'));
     $ppg = $ppm->type_obj()->get_gateway();
     $ppg->set_settings(array('paypal_id' => $this->_paypal_id, 'paypal_taxes' => TRUE, 'paypal_shipping' => TRUE));
     $t = $this->new_typical_transaction();
     $p = $this->new_model_obj_with_dependencies('Payment', array('TXN_ID' => $t->ID(), 'PMD_ID' => $ppm->ID(), 'PAY_amount' => $t->total()));
     $old_pretax_total = EEH_Line_Item::get_items_subtotal($t->total_line_item())->total();
     $old_taxable_total = $t->total_line_item()->taxable_total();
     $this->assertNotEmpty($old_taxable_total);
     $old_tax_total = $t->tax_total();
     $this->assertNotEmpty($old_tax_total);
     //skip IPN validation with paypal
     add_filter('FHEE__EEG_Paypal_Standard__validate_ipn__skip', '__return_true');
     $ipn_args = array('e_reg_url_link' => '1-203446311152995f326e9ca81b64c95b', 'mc_gross' => $old_pretax_total + 8 + 2.8, 'protection_eligibility' => 'Eligible', 'address_status' => 'confirmed', 'item_number1' => '', 'item_number2' => '', 'payer_id' => 'DQUX5EF8CFFQ2', 'tax' => '2.80', 'address_street' => '1 Maire-Victorin', 'payment_date' => '15:21:18 Jul 04, 2014 PDT', 'payment_status' => 'Completed', 'option_selection1_2' => 'http://localhost/wp-develop/src/transactions/?e_reg_url_link=1-203446311152995f326e9ca81b64c95b&ee_payment_method=paypal_standard', 'charset' => 'windows-1252', 'address_zip' => 'M5A 1E1', 'mc_shipping' => '8.00', 'mc_handling' => '0.00', 'first_name' => 'canadaman', 'mc_fee' => '1.50', 'address_country_code' => 'CA', 'address_name' => 'canadaman eh', 'notify_version' => '3.8', 'custom' => '', 'payer_status' => 'verified', 'business' => '*****@*****.**', 'address_country' => 'Canada', 'num_cart_items' => '2', 'mc_handling1' => '0.00', 'mc_handling2' => '0.00', 'address_city' => 'Toronto', 'payer_email' => '*****@*****.**', 'verify_sign' => 'Asuc-38eoonqdqSbDHczw6533JekAJTc2w.QEYe.bLdd3C9Sk1FmgQur', 'mc_shipping1' => '0.00', 'mc_shipping2' => '0.00', 'tax1' => '0.00', 'tax2' => '0.00', 'option_name1_2' => 'NOTIFY URL', 'txn_id' => '4W884024TK795542J', 'payment_type' => 'instant', 'last_name' => 'eh', 'item_name1' => 'Free Ticket', 'address_state' => 'Ontario', 'receiver_email' => '*****@*****.**', 'item_name2' => 'DEBUG INFO (this item only added in sandbox mode', 'payment_fee' => '1.50', 'shipping_discount' => '0.00', 'quantity1' => '1', 'insurance_amount' => '0.00', 'quantity2' => '1', 'receiver_id' => '8STUBD4V9ZUUN', 'txn_type' => 'cart', 'discount' => '0.00', 'mc_gross_1' => '20.00', 'mc_currency' => 'USD', 'mc_gross_2' => '0.00', 'residence_country' => 'CA', 'test_ipn' => '1', 'shipping_method' => 'International Economy', 'transaction_subject' => '', 'payment_gross' => '30.80', 'auth' => 'A7v0XCv0MTRMLTC3ib4B4zYtTI7Wt-pU5StpnoQBIGsiMj5pXBoOr8z8kiKzYdNkeTmwiWW3xlus4rZhBUOqj6g');
     $ppg->handle_payment_update($ipn_args, $t);
     $ppg->update_txn_based_on_payment($p);
     //check the new tax is correct
     $this->assertNotEquals($old_tax_total, $t->tax_total(), 'Its not necessarily wrong for the old tax to match the new tax; but if they match we can\'t be very sure the tax total was updated');
     $this->assertEquals(floatval($ipn_args['tax']), $t->tax_total());
     $tax_line_items = EEH_Line_Item::get_taxes_subtotal($t->total_line_item())->children();
     $this->assertEquals(1, count($tax_line_items));
     $only_tax = array_shift($tax_line_items);
     $this->assertEquals(__('Taxes', 'event_espresso'), $only_tax->name());
     $this->assertEquals(EEM_Payment::status_id_approved, $p->status());
     $this->assertEquals($t->total(), $p->amount());
     //check that the shipping surcharge is correct
     $items_subtotal = EEH_Line_Item::get_items_subtotal($t->total_line_item());
     $items = $items_subtotal->children();
     $first_item = array_shift($items);
     $this->assertEquals(10, $first_item->total());
     $second_item = array_shift($items);
     $this->assertEquals(8, $second_item->total());
     //EEH_Line_Item::visualize($t->total_line_item());
     $this->assertEquals($old_pretax_total + 8, $items_subtotal->total());
     //check that the transaction's total got updated to match the total line item's
     $this->assertEquals($t->total_line_item()->total(), $t->total());
     //check that if we re-calculate all the prices everything is still the same
     $updated_line_item_total = $t->total_line_item()->total();
     $t->total_line_item()->recalculate_total_including_taxes();
     $this->assertEquals($updated_line_item_total, $t->total_line_item()->total());
 }
 /**
  * Gets the total line item (which is a parent of all other related line items,
  * meaning it takes them all into account on its total)
  * @return EE_Line_Item
  */
 public function total_line_item()
 {
     $item = $this->get_first_related('Line_Item', array(array('LIN_type' => EEM_Line_Item::type_total)));
     if (!$item) {
         EE_Registry::instance()->load_helper('Line_Item');
         $item = EEH_Line_Item::create_total_line_item($this);
     }
     return $item;
 }
 /**
  * @deprecated
  * @param string $type like one of the EEM_Line_Item::type_*
  * @return EE_Line_Item
  */
 public function get_nearest_descendant_of_type($type)
 {
     EE_Error::doing_it_wrong('EE_Line_Item::get_nearest_descendant_of_type()', __('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'), '4.6.0');
     return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
 }
 /**
  * @deprecated
  * @param string $type like one of the EEM_Line_Item::type_*
  * @return EE_Line_Item
  */
 public function get_nearest_descendant_of_type($type)
 {
     EE_Error::doing_it_wrong('EE_Line_Item::get_nearest_descendant_of_type()', __('Method replaced with EEH_Line_Item::get_nearest_descendant_of_type()', 'event_espresso'), '4.6.0');
     EE_Registry::instance()->load_helper('Line_Item');
     return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
 }
 /**
  *	@save cart to session
  *	@access public
  *	@return TRUE on success, FALSE on fail
  */
 public function save_cart()
 {
     EEH_Line_Item::ensure_taxes_applied($this->_grand_total);
     return EE_Registry::instance()->SSN->set_cart($this);
 }
 /**
  * 	test_set_tax
  */
 function test_set_tax()
 {
     //first create a line item
     $txn = $this->new_typical_transaction();
     $line_item = $txn->total_line_item();
     $old_tax_subtotal = $line_item->get_nearest_descendant_of_type(EEM_Line_Item::type_tax_sub_total);
     $this->assertInstanceOf('EE_Line_Item', $old_tax_subtotal);
     $old_tax = $old_tax_subtotal->get_nearest_descendant_of_type(EEM_Line_Item::type_tax);
     $new_tax = EEH_Line_Item::set_total_tax_to($line_item, 1.5, 'Monkey Tax', 'Only monkey must pay');
     $this->assertEquals(1.5, $new_tax->total());
     $this->assertEquals($new_tax->total(), $old_tax_subtotal->total());
     $child_of_tax_subtotal = $old_tax_subtotal->get_nearest_descendant_of_type(EEM_Line_Item::type_tax);
     $this->assertEquals($new_tax, $child_of_tax_subtotal);
     $tax_total_before_recalculation = $old_tax_subtotal->total();
     $tax_before_recalculations = $new_tax->total();
     $line_item->recalculate_taxes_and_tax_total();
     $this->assertEquals($tax_before_recalculations, $new_tax->total());
     $this->assertEquals($tax_total_before_recalculation, $old_tax_subtotal->total());
 }
 /**
  * @return EE_Form_Section_Proper
  */
 public function generate_reg_form()
 {
     $this->_print_copy_info = FALSE;
     $primary_registrant = NULL;
     // autoload Line_Item_Display classes
     EEH_Autoloader::register_line_item_display_autoloaders();
     $Line_Item_Display = new EE_Line_Item_Display();
     // calculate taxes
     $Line_Item_Display->display_line_item($this->checkout->cart->get_grand_total(), array('set_tax_rate' => true));
     EE_Registry::instance()->load_helper('Line_Item');
     /** @var $subsections EE_Form_Section_Proper[] */
     $subsections = array('default_hidden_inputs' => $this->reg_step_hidden_inputs());
     $template_args = array('revisit' => $this->checkout->revisit, 'registrations' => array(), 'ticket_count' => array());
     // grab the saved registrations from the transaction
     $registrations = $this->checkout->transaction->registrations($this->checkout->reg_cache_where_params);
     if ($registrations) {
         foreach ($registrations as $registration) {
             if ($registration instanceof EE_Registration) {
                 // can this registration be processed during this visit ?
                 if ($this->checkout->visit_allows_processing_of_this_registration($registration)) {
                     $subsections[$registration->reg_url_link()] = $this->_registrations_reg_form($registration);
                     if (!$this->checkout->admin_request) {
                         $template_args['registrations'][$registration->reg_url_link()] = $registration;
                         $template_args['ticket_count'][$registration->ticket()->ID()] = isset($template_args['ticket_count'][$registration->ticket()->ID()]) ? $template_args['ticket_count'][$registration->ticket()->ID()] + 1 : 1;
                         $ticket_line_item = EEH_Line_Item::get_line_items_by_object_type_and_IDs($this->checkout->cart->get_grand_total(), 'Ticket', array($registration->ticket()->ID()));
                         $ticket_line_item = is_array($ticket_line_item) ? reset($ticket_line_item) : $ticket_line_item;
                         $template_args['ticket_line_item'][$registration->ticket()->ID()] = $Line_Item_Display->display_line_item($ticket_line_item);
                     }
                     if ($registration->is_primary_registrant()) {
                         $primary_registrant = $registration->reg_url_link();
                     }
                 }
             }
         }
         // print_copy_info ?
         if ($primary_registrant && count($registrations) > 1 && !$this->checkout->admin_request) {
             // TODO: add admin option for toggling copy attendee info, then use that value to change $this->_print_copy_info
             $copy_options['spco_copy_attendee_chk'] = $this->_print_copy_info ? $this->_copy_attendee_info_form() : $this->_auto_copy_attendee_info();
             // generate hidden input
             if (isset($subsections[$primary_registrant]) && $subsections[$primary_registrant] instanceof EE_Form_Section_Proper) {
                 $subsections[$primary_registrant]->add_subsections($copy_options, 'primary_registrant', false);
             }
         }
     }
     return new EE_Form_Section_Proper(array('name' => $this->reg_form_name(), 'html_id' => $this->reg_form_name(), 'subsections' => $subsections, 'layout_strategy' => $this->checkout->admin_request ? new EE_Div_Per_Section_Layout() : new EE_Template_Layout(array('layout_template_file' => $this->_template, 'template_args' => $template_args))));
 }
 /**
  * Returns a modified line item tree where all the subtotals which have a total of 0
  * are removed, and line items with a quantity of 0
  *
  * @param EE_Line_Item $line_item |null
  * @return \EE_Line_Item|null
  */
 public static function non_empty_line_items(EE_Line_Item $line_item)
 {
     $copied_li = EEH_Line_Item::non_empty_line_item($line_item);
     if ($copied_li === null) {
         return null;
     }
     //if this is an event subtotal, we want to only include it if it
     //has a non-zero total and at least one ticket line item child
     $ticket_children = 0;
     foreach ($line_item->children() as $child_li) {
         $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
         if ($child_li_copy !== null) {
             $copied_li->add_child_line_item($child_li_copy);
             if ($child_li_copy->type() === EEM_Line_Item::type_line_item && $child_li_copy->OBJ_type() === 'Ticket') {
                 $ticket_children++;
             }
         }
     }
     //if this is an event subtotal with NO ticket children
     //we basically want to ignore it
     if ($line_item->type() === EEM_Line_Item::type_sub_total && $line_item->OBJ_type() === 'Event' && $ticket_children === 0 && $line_item->total() === 0) {
         return null;
     }
     return $copied_li;
 }
 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();
 }
 /**
  * @group current
  * @group 4710
  */
 function test_set_line_items_taxable()
 {
     $t = $this->new_typical_transaction(array('taxable_tickets' => 0));
     EEH_Line_Item::add_unrelated_item($t->total_line_item(), 'Excempt Line Item', 1, 'Description', 1, false, 'exemptme');
     $reg_line_items = EEH_Line_Item::get_descendants_of_type($t->total_line_item(), EEM_Line_Item::type_line_item);
     foreach ($reg_line_items as $line_item) {
         $this->assertFalse($line_item->is_taxable(), print_r($line_item->model_field_array(), true));
     }
     EEH_Line_Item::set_line_items_taxable($t->total_line_item(), true, 'exemptme');
     foreach ($reg_line_items as $line_item) {
         if ($line_item->code() == 'exemptme') {
             $this->assertFalse($line_item->is_taxable(), print_r($line_item->model_field_array(), true));
         } else {
             $this->assertTrue($line_item->is_taxable(), print_r($line_item->model_field_array(), true));
         }
     }
 }
 /**
  * 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;
 }
 /**
  * Deletes ALL children of the passed line item
  *
  * @param EE_Line_Item $parent_line_item
  * @return bool
  */
 public static function delete_all_child_items(EE_Line_Item $parent_line_item)
 {
     $deleted = 0;
     foreach ($parent_line_item->children() as $child_line_item) {
         if ($child_line_item instanceof EE_Line_Item) {
             $deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
             if ($child_line_item->ID()) {
                 $child_line_item->delete();
             } else {
                 $parent_line_item->delete_child_line_item($child_line_item->code());
             }
             $deleted++;
         }
     }
     return $deleted;
 }