function test_generate_code()
 {
     $t = EE_Transaction::new_instance();
     $t->save();
     $l = EE_Line_Item::new_instance(array('OBJ_type' => 'Transaction', 'OBJ_ID' => $t->ID()));
     $this->assertNotNull($l->generate_code());
 }
 function test_finalize()
 {
     $t = EE_Transaction::new_instance(array('STS_ID' => EEM_Transaction::complete_status_code));
     $t->save();
     $e = EE_Event::new_instance();
     $e->save();
     $tkt = EE_Ticket::new_instance();
     $tkt->save();
     $d = EE_Datetime::new_instance(array('EVT_ID' => $e->ID()));
     $d->save();
     $tkt->_add_relation_to($d, 'Datetime');
     /** @type EE_Registration_Processor $registration_processor */
     $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
     $reg_url = $registration_processor->generate_reg_url_link(1, EE_Line_Item::new_instance(array('LIN_name' => $tkt->name(), 'LIN_desc' => $tkt->description(), 'LIN_unit_price' => $tkt->price(), 'LIN_quantity' => 1, 'LIN_is_taxable' => $tkt->taxable(), 'LIN_order' => 0, 'LIN_total' => $tkt->price(), 'LIN_type' => EEM_Line_Item::type_line_item, 'OBJ_ID' => $tkt->ID(), 'OBJ_type' => 'Ticket')));
     $r = EE_REgistration::new_instance(array('EVT_ID' => $e->ID(), 'TXN_ID' => $t->ID(), 'TKT_ID' => $tkt->ID(), 'STS_ID' => EEM_Registration::status_id_pending_payment, 'REG_url_link' => $reg_url));
     $r->set_reg_code($registration_processor->generate_reg_code($r));
     $registration_processor->update_registration_after_checkout_or_payment($r);
     $this->assertNotNull($r->reg_code());
     $this->assertEquals(EEM_Registration::status_id_approved, $r->status_ID());
 }
 /**
  */
 function test_filter__removed_subtotals_with_no_ticket_children()
 {
     //verify that normal subtotals stay, but subtotals with no ticket children get removed
     //so create a total
     $total = EE_Line_Item::new_instance(array('LIN_name' => 'total', 'LIN_code' => 'total', 'LIN_type' => EEM_Line_Item::type_total));
     //and a subttoal WITH a ticket (but the subtotal itself was erroneously labelled as quantity 0)
     $subtotal_with_tkt = EE_Line_Item::new_instance(array('LIN_name' => 'subtotal-with-tkt', 'LIN_code' => 'subttoal-with-tkt', 'LIN_type' => EEM_Line_Item::type_sub_total, 'LIN_quantity' => 0));
     $total->add_child_line_item($subtotal_with_tkt);
     $tkt1 = EE_Line_Item::new_instance(array('LIN_name' => 'tkt1', 'LIN_code' => 'tkt1', 'LIN_type' => EEM_Line_Item::type_line_item, 'OBJ_type' => 'Ticket', 'LIN_quantity' => 1));
     $subtotal_with_tkt->add_child_line_item($tkt1);
     //and another subtotal with a ticket but quantity 0, and another non-ticket line item with quantity 1
     $subtotal_without_tkt = EE_Line_Item::new_instance(array('LIN_name' => 'subtotal-without-tkt', 'LIN_code' => 'subttoal-without-tkt', 'LIN_type' => EEM_Line_Item::type_sub_total, 'LIN_quantity' => 0));
     $total->add_child_line_item($subtotal_without_tkt);
     $tkt2 = EE_Line_Item::new_instance(array('LIN_name' => 'tkt2', 'LIN_code' => 'tkt2', 'LIN_type' => EEM_Line_Item::type_line_item, 'OBJ_type' => 'Ticket', 'LIN_quantity' => 0));
     $subtotal_without_tkt->add_child_line_item($tkt2);
     $non_tkt_li = EE_Line_Item::new_instance(array('LIN_name' => 'non_tkt_li', 'LIN_code' => 'non_tkt_li', 'LIN_type' => EEM_Line_Item::type_line_item, 'OBJ_type' => null, 'LIN_quantity' => 1));
     $subtotal_without_tkt->add_child_line_item($non_tkt_li);
     //only the subtotal without a ticket should get filtered out
     $filter = new EE_Non_Zero_Line_Item_Filter();
     $filter->process($total);
     $totals_children = $total->children();
     $this->assertContains($subtotal_with_tkt, $totals_children);
     $this->assertNotContains($subtotal_without_tkt, $totals_children);
 }
 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();
 }
 /**
  * get_redeemable_scope_promos
  * searches the cart for any items that this promotion applies to
  *
  * @since   1.0.0
  *
  * @param EE_Line_Item $parent_line_item the line item to create the new promotion line item under
  * @param EE_Promotion $promotion        the promotion object that the line item is being created for
  * @param string       $promo_name
  * @param bool         $affects_tax
  * @return \EE_Line_Item
  * @throws \EE_Error
  */
 public function generate_promotion_line_item(EE_Line_Item $parent_line_item, EE_Promotion $promotion, $promo_name = '', $affects_tax = false)
 {
     // verify EE_Line_Item
     if (!$parent_line_item instanceof EE_Line_Item) {
         throw new EE_Error(__('A valid EE_Line_Item object is required to generate a promotion line item.', 'event_espresso'));
     }
     // verify EE_Promotion
     if (!$promotion instanceof EE_Promotion) {
         throw new EE_Error(__('A valid EE_Promotion object is required to generate a promotion line item.', 'event_espresso'));
     }
     $promo_name = !empty($promo_name) ? $promo_name : $promotion->name();
     $promo_desc = $promotion->price()->desc();
     $promo_desc .= $promotion->code() != '' ? ' ( ' . $promotion->code() . ' )' : '';
     // generate promotion line_item
     $line_item = EE_Line_Item::new_instance(array('LIN_code' => 'promotion-' . $promotion->ID(), 'TXN_ID' => $parent_line_item->TXN_ID(), 'LIN_name' => apply_filters('FHEE__EE_Promotion_Scope__generate_promotion_line_item__LIN_name', $promo_name, $promotion), 'LIN_desc' => $promo_desc, 'LIN_unit_price' => $promotion->is_percent() ? 0 : $promotion->amount(), 'LIN_percent' => $promotion->is_percent() ? $promotion->amount() : 0, 'LIN_is_taxable' => $affects_tax, 'LIN_order' => $promotion->price()->order() + EE_Promotion_Scope::$_counter, 'LIN_total' => $promotion->calculated_amount_on_value($parent_line_item->total()), 'LIN_quantity' => 1, 'LIN_parent' => $parent_line_item->ID(), 'LIN_type' => $this->get_promotion_line_item_type(), 'OBJ_ID' => $promotion->ID(), 'OBJ_type' => 'Promotion'));
     EE_Promotion_Scope::$_counter++;
     return $line_item;
 }
 /**
  *
  */
 function test_process__2_events_some_taxed_with_discounts()
 {
     $grand_total = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_total, 'LIN_name' => 'Total'));
     $pretax_total = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_sub_total, 'LIN_code' => 'pretax', 'LIN_name' => 'pretax', 'LIN_order' => 1));
     $grand_total->add_child_line_item($pretax_total);
     $tax_total = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_tax_sub_total, 'LIN_code' => 'tax-total', 'LIN_name' => 'tax-total', 'LIN_order' => 1000));
     $grand_total->add_child_line_item($tax_total);
     $a_tax = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_tax, 'LIN_code' => 'tax', 'LIN_name' => 'tax', 'LIN_percent' => 10, 'LIN_order' => 1000));
     $tax_total->add_child_line_item($a_tax);
     $event_A_subtotal = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_sub_total, 'LIN_code' => 'eventA', 'LIN_name' => 'eventA', 'LIN_order' => 1));
     $pretax_total->add_child_line_item($event_A_subtotal);
     $event_B_subtotal = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_sub_total, 'LIN_code' => 'eventB', 'LIN_name' => 'eventB', 'LIN_order' => 2));
     $pretax_total->add_child_line_item($event_B_subtotal);
     $ticket_quantities = array(1 => array('included' => 4, 'not' => 6), 2 => array('included' => 0, 'not' => 2), 3 => array('included' => 1, 'not' => 0));
     $ticket_1_li = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_line_item, 'LIN_code' => 'ticket1', 'LIN_name' => 'ticket1', 'OBJ_ID' => 1, 'OBJ_type' => 'Ticket', 'LIN_unit_price' => 10, 'LIN_quantity' => $ticket_quantities[1]['included'] + $ticket_quantities[1]['not'], 'LIN_total' => 100, 'LIN_is_taxable' => false, 'LIN_order' => 1));
     $event_A_subtotal->add_child_line_item($ticket_1_li);
     $ticket_2_li = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_line_item, 'LIN_code' => 'ticket2', 'LIN_name' => 'ticket2', 'OBJ_ID' => 2, 'OBJ_type' => 'Ticket', 'LIN_unit_price' => 50, 'LIN_quantity' => $ticket_quantities[2]['included'] + $ticket_quantities[2]['not'], 'LIN_total' => 50, 'LIN_is_taxable' => true, 'LIN_order' => 2));
     $event_A_subtotal->add_child_line_item($ticket_2_li);
     $percent_discount_li = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_line_item, 'LIN_code' => 'discount-percent', 'LIN_name' => 'discount-percent', 'LIN_unit_price' => 0, 'LIN_percent' => -25, 'LIN_total' => -50, 'LIN_quantity' => 1, 'LIN_order' => 3));
     $event_A_subtotal->add_child_line_item($percent_discount_li);
     $flat_discount_li = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_line_item, 'LIN_code' => 'discount-flat', 'LIN_name' => 'discount-flat', 'LIN_unit_price' => 0, 'LIN_percent' => 0, 'LIN_unit_price' => -10, 'LIN_total' => -10, 'LIN_quantity' => 1, 'LIN_order' => 4));
     $event_A_subtotal->add_child_line_item($flat_discount_li);
     //and add something to event B
     $ticket_3_li = EE_Line_Item::new_instance(array('LIN_type' => EEM_Line_Item::type_line_item, 'LIN_code' => 'ticket3', 'LIN_name' => 'ticket3', 'OBJ_ID' => 3, 'OBJ_type' => 'Ticket', 'LIN_unit_price' => 35, 'LIN_quantity' => $ticket_quantities[3]['included'] + $ticket_quantities[3]['not'], 'LIN_total' => 35, 'LIN_is_taxable' => true));
     $event_B_subtotal->add_child_line_item($ticket_3_li);
     //also need to make registrations
     $regs_to_include = $this->_create_regs($ticket_quantities, $grand_total);
     //ok now let's use the filter
     $filter = new EE_Specific_Registrations_Line_Item_Filter($regs_to_include);
     $filtered_total = $filter->process($grand_total);
     //		echo "AFTER tree:";
     //		EEH_Line_Item::visualize( $grand_total );
     //the filter doesn't recalculate totals, and it edits the inputted tree;
     //hat's ok, the processor does both of those. But we need to manually do it here
     $this->assertEquals($grand_total, $filtered_total);
     $grand_total->recalculate_total_including_taxes();
     //check ticket 1
     $this->assertEquals(4, $ticket_1_li->quantity());
     $this->assertEquals(10, $ticket_1_li->unit_price());
     $this->assertEquals(40, $ticket_1_li->total());
     //check ticket 2
     $this->assertEquals(0, $ticket_2_li->quantity());
     $this->assertEquals(50, $ticket_2_li->unit_price());
     $this->assertEquals(0, $ticket_2_li->total());
     //and verify the percent line item's total has changed, but not its percent
     $this->assertEquals(-25, $percent_discount_li->percent());
     $this->assertEquals(-10, $percent_discount_li->total());
     //flat discount is tricky. we only show the PART of the flat discount
     //that applied to the tickets shown. To determine that, we see what
     //percent of the original discount was of the original total, and then multiply
     //the new total by that percent.
     //the original total = ticket1's-total + ticket2's-total + percent-discount
     // = 100 + 100 - 50 = 150
     //flat discount's original percent of total = flat-discount / original total
     // = -10 / 150 = 0.0666666
     //new total = ticket1's-filtered-total + ticket2's-filtered-total + percent-discount-for-filtered-total
     // = 40 + 0 - 10 = 30
     //new flat total = flat discount's original percent of total x new total
     // = 0.0666666 x 30 = 1.9999999
     $this->assertEquals(1, $flat_discount_li->quantity());
     $this->assertEquals(-2, $flat_discount_li->unit_price());
     $this->assertEquals(-2, $flat_discount_li->total());
     //other event's ticket purchase
     $this->assertEquals(1, $ticket_3_li->quantity());
     $this->assertEquals(35, $ticket_3_li->unit_price());
     $this->assertEquals(35, $ticket_3_li->total());
 }
 /**
  * Creates a new, unsaved line item, but if it's a ticket line item
  * with a total of 0, or a subtotal of 0, returns null instead
  * @param EE_Line_Item      $line_item
  * @return EE_Line_Item
  */
 public static function non_empty_line_item(EE_Line_Item $line_item)
 {
     if ($line_item->type() === EEM_Line_Item::type_line_item && $line_item->OBJ_type() === 'Ticket' && $line_item->quantity() == 0) {
         return null;
     }
     $new_li_fields = $line_item->model_field_array();
     //don't set the total. We'll leave that up to the code that calculates it
     unset($new_li_fields['LIN_ID']);
     unset($new_li_fields['LIN_parent']);
     return EE_Line_Item::new_instance($new_li_fields);
 }
 /**
  * @group 7358
  */
 public function test_get_raw()
 {
     $l2 = EE_Line_Item::new_instance(array());
     $this->assertTrue(1 == $l2->get_raw('LIN_quantity'));
     $l2->save();
     $l2_from_db = EEM_Line_Item::reset()->get_one_by_ID($l2->ID());
     //double check its NULL in the DB
     $qty_col_with_one_result = EEM_Line_Item::instance()->get_col(array(array('LIN_ID' => $l2->ID())), 'LIN_quantity');
     $qty_col_in_db = reset($qty_col_with_one_result);
     $this->assertTrue(1 == $qty_col_in_db);
     //and now verify get_raw is returning that same value
     $this->assertTrue(1 == $l2_from_db->get_raw('LIN_quantity'));
 }
 /**
  * Verifies that we fix sub line item quantities and line item unit prices
  * @group 8566
  */
 function test_recalculate_total_including_taxes__fix_sub_line_item_quantities()
 {
     $line_item = EE_Line_Item::new_instance(array('LIN_name' => 'ticket', 'LIN_type' => EEM_Line_Item::type_line_item, 'LIN_quantity' => 2));
     $line_item->save();
     $flat_sub_line_item = EE_Line_Item::new_instance(array('LIN_name' => 'flat', 'LIN_type' => EEM_Line_Item::type_sub_line_item, 'LIN_unit_price' => 10, 'LIN_quantity' => 1, 'LIN_order' => 1, 'LIN_parent' => $line_item->ID()));
     $flat_sub_line_item->save();
     $percent_sub_line_item = EE_Line_Item::new_instance(array('LIN_name' => 'percent', 'LIN_type' => EEM_Line_Item::type_sub_line_item, 'LIN_quantity' => 0, 'LIN_percent' => -25, 'LIN_order' => 100, 'LIN_parent' => $line_item->ID()));
     $percent_sub_line_item->save();
     $line_item->recalculate_pre_tax_total();
     $this->assertEquals(2, $flat_sub_line_item->quantity());
     $this->assertEquals(1, $percent_sub_line_item->quantity());
     $this->assertEquals(20, $flat_sub_line_item->total());
     $this->assertEquals(-5, $percent_sub_line_item->total());
     $this->assertEquals(15, $line_item->total());
     $this->assertEquals(7.5, $line_item->unit_price());
 }
 /**
  * Simply adds global taxes to the cart's taxes (if they were already in there, updates them).
  * Doesn't calculate the totals for each tax or add the tax totals to the total line item
  * @return void
  */
 private function _add_taxes_to_cart()
 {
     // start with empty array
     $this->_taxes = array();
     // get array of taxes via Price Model
     $ordered_taxes = EE_Registry::instance()->load_model('Price')->get_all_prices_that_are_taxes();
     ksort($ordered_taxes);
     $taxes_line_item = $this->get_taxes_line_item();
     //just to be safe, remove its old tax line items
     $taxes_line_item->delete_children_line_items();
     //loop thru taxes
     foreach ($ordered_taxes as $order => $taxes) {
         foreach ($taxes as $tax) {
             if ($tax instanceof EE_Price) {
                 $taxes_line_item->add_child_line_item(EE_Line_Item::new_instance(array('LIN_name' => $tax->name(), 'LIN_desc' => $tax->desc(), 'LIN_percent' => $tax->amount(), 'LIN_is_taxable' => false, 'LIN_order' => $order, 'LIN_total' => 0, 'LIN_type' => EEM_Line_Item::type_tax, 'OBJ_type' => 'Price', 'OBJ_ID' => $tax->ID())));
             }
         }
     }
 }
 /**
  * Overwrites the previous tax by clearing out the old taxes, and creates a new
  * tax and updates the total line item accordingly
  * @param EE_Line_Item $total_line_item
  * @param float $amount
  * @param string $name
  * @param string $description
  * @return EE_Line_Item the new tax line item created
  */
 public static function set_total_tax_to(EE_Line_Item $total_line_item, $amount, $name = NULL, $description = NULL)
 {
     //first: remove all tax descendants
     //add this as a new tax descendant
     $tax_subtotal = self::get_taxes_subtotal($total_line_item);
     $tax_subtotal->delete_children_line_items();
     $taxable_total = $total_line_item->taxable_total();
     $new_tax = EE_Line_Item::new_instance(array('TXN_ID' => $total_line_item->TXN_ID(), 'LIN_name' => $name ? $name : __('Tax', 'event_espresso'), 'LIN_desc' => $description ? $description : '', 'LIN_percent' => $taxable_total ? $amount / $total_line_item->taxable_total() * 100 : 0, 'LIN_total' => $amount, 'LIN_parent' => $tax_subtotal->ID(), 'LIN_type' => EEM_Line_Item::type_tax));
     $new_tax->save();
     $tax_subtotal->set_total($amount);
     $tax_subtotal->save();
     $total_line_item->recalculate_total_including_taxes();
     return $new_tax;
 }