/** * @param EE_Line_Item $line_item * @param array $options * @return mixed */ public function display_line_item(EE_Line_Item $line_item, $options = array()) { EE_Registry::instance()->load_helper('HTML'); $html = ''; // set some default options and merge with incoming $default_options = array('show_desc' => TRUE, 'odd' => FALSE); $options = array_merge($default_options, (array) $options); switch ($line_item->type()) { case EEM_Line_Item::type_total: // loop thru children foreach ($line_item->children() as $child_line_item) { // recursively feed children back into this method // $html .= $this->display_line_item( $child_line_item, $options ); } // $html .= $this->_separator_row( $options ); // $html .= $this->_total_row( $line_item, __('Total', 'event_espresso'), $options ); break; case EEM_Line_Item::type_sub_total: // loop thru children foreach ($line_item->children() as $child_line_item) { // recursively feed children back into this method // $html .= $this->display_line_item( $child_line_item, $options ); } // $html .= $this->_total_row( $line_item, __('Sub-Total', 'event_espresso'), $options ); break; case EEM_Line_Item::type_tax_sub_total: // loop thru children foreach ($line_item->children() as $child_line_item) { // recursively feed children back into this method // $html .= $this->display_line_item( $child_line_item, $options ); } // $html .= $this->_total_row( $line_item, __('Tax Total', 'event_espresso'), $options ); break; case EEM_Line_Item::type_line_item: // item row // $html .= $this->_item_row( $line_item, $options ); // got any kids? foreach ($line_item->children() as $child_line_item) { // $this->display_line_item( $child_line_item, $options ); } break; case EEM_Line_Item::type_sub_line_item: // $html .= $this->_sub_item_row( $line_item, $options ); break; case EEM_Line_Item::type_tax: // $html .= $this->_tax_row( $line_item, $options ); break; } return $html; }
/** * @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) { $datetimes = $ticket->datetimes(); $event_names = array(); foreach ($datetimes as $datetime) { $event = $datetime->event(); $event_names[$event->ID()] = $event->name(); } $description_addition = " (For " . implode(", ", $event_names) . ")"; $full_description = $ticket->description() . $description_addition; // add $ticket to cart $line_item = EE_Line_Item::new_instance(array('LIN_name' => $ticket->name(), 'LIN_desc' => $full_description, 'LIN_unit_price' => $ticket->price(), 'LIN_quantity' => $qty, 'LIN_is_taxable' => $ticket->taxable(), 'LIN_order' => count($this->_grand_total->children()), 'LIN_total' => $ticket->price() * $qty, 'LIN_type' => EEM_Line_Item::type_line_item, 'OBJ_ID' => $ticket->ID(), 'OBJ_type' => 'Ticket')); //now add the sub-line items $running_total_for_ticket = 0; foreach ($ticket->prices(array('order_by' => array('PRC_order' => 'ASC'))) as $price) { $sign = $price->is_discount() ? -1 : 1; $price_total = $price->is_percent() ? $running_total_for_ticket * $price->amount() / 100 : $price->amount() * $qty; $sub_line_item = EE_Line_Item::new_instance(array('LIN_name' => $price->name(), 'LIN_desc' => $price->desc(), 'LIN_quantity' => $price->is_percent() ? null : $qty, 'LIN_is_taxable' => false, 'LIN_order' => $price->order(), 'LIN_total' => $sign * $price_total, 'LIN_type' => EEM_Line_Item::type_sub_line_item, 'OBJ_ID' => $price->ID(), 'OBJ_type' => 'Price')); if ($price->is_percent()) { $sub_line_item->set_percent($sign * $price->amount()); } else { $sub_line_item->set_unit_price($sign * $price->amount()); } $running_total_for_ticket += $price_total; $line_item->add_child_line_item($sub_line_item); } $this->_add_item($line_item); return $this->save_cart() ? TRUE : FALSE; }
/** * Recursive function for traversing all the sub-items of each line item * and displaying them in the table * @param EE_Line_Item $line_item * @param boolean $odd for indicating whether to style this line item as an 'odd' or 'even' */ function ee_invoice_display_line_item(EE_Line_Item $line_item, $show_line_item_description, $odd = false) { switch ($line_item->type()) { case EEM_Line_Item::type_total: foreach ($line_item->children() as $child_line_item) { ee_invoice_display_line_item($child_line_item, $show_line_item_description); } ?> <tr><td colspan="<?php echo $show_line_item_description ? 5 : 4; ?> "><hr></td></tr> <tr class="total_tr odd"> <td colspan="<?php echo $show_line_item_description ? 2 : 1; ?> "> </td> <td colspan="2" class="total" id="total_currency"><?php _e('Total', 'event_espresso'); ?> </td> <td class="total"><?php echo $line_item->total_no_code(); ?> </td> </tr> <?php break; case EEM_Line_Item::type_sub_total: foreach ($line_item->children() as $child_line_item) { //$odd = !$odd; ee_invoice_display_line_item($child_line_item, $show_line_item_description, $odd); } ?> <tr class="total_tr odd"> <td colspan="<?php echo $show_line_item_description ? 2 : 1; ?> "> </td> <td colspan="2" class="total" id="total_currency"><?php _e('Sub-Total', 'event_espresso'); ?> </td> <td class="total"><?php echo $line_item->total_no_code(); ?> </td> </tr> <?php break; case EEM_Line_Item::type_tax_sub_total: foreach ($line_item->children() as $child_line_item) { $odd = !$odd; ee_invoice_display_line_item($child_line_item, $show_line_item_description, $odd); } ?> <tr class="total_tr odd"> <td colspan="<?php echo $show_line_item_description ? 2 : 1; ?> "> </td> <td colspan="2" class="total" id="total_currency"><?php _e('Tax Total', 'event_espresso'); ?> </td> <td class="total"><?php echo $line_item->total_no_code(); ?> </td> </tr> <?php break; case EEM_Line_Item::type_line_item: $subitems = $line_item->children(); $has_subitems = count($subitems) > 1; if ($has_subitems) { ?> <tr class="item <?php echo $odd ? 'odd' : ''; ?> "> <td class="item_l"><?php echo $line_item->name(); ?> </td> <?php if ($show_line_item_description) { ?> <td class="item_l"><?php echo $line_item->desc(); ?> </td><?php } ?> <td class="item_l"><?php echo $line_item->quantity(); ?> </td> <td class="item_c"><?php echo $line_item->unit_price_no_code(); ?> </td> <td class="item_r"> <?php echo $line_item->total_no_code(); echo $line_item->is_taxable() ? '*' : ''; ?> </td> <?php //<td class="item_l"><?php $datetimes_strings = array(); foreach($datetimes as $datetime){ $datetimes_strings[]= $datetime->start_date_and_time();} echo implode(", ",$datetimes_strings); ?> </tr> <?php if ($has_subitems) { foreach ($line_item->children() as $child_line_item) { ee_invoice_display_line_item($child_line_item, $show_line_item_description, $odd); } } } else { //no subitems - just show this line item ?> <tr class="item <?php echo $odd ? 'odd' : ''; ?> "> <td class="item_l"><?php echo $line_item->name(); ?> </td> <?php if ($show_line_item_description) { ?> <td class="item_l"><?php echo $line_item->desc(); ?> </td><?php } ?> <td class="item_l"><?php echo $line_item->quantity(); ?> </td> <td class="item_c"><?php echo $line_item->unit_price_no_code(); ?> </td> <td class="item_r"> <?php echo $line_item->total_no_code(); echo $line_item->is_taxable() ? '*' : ''; ?> </td> <?php //<td class="item_l"><?php $datetimes_strings = array(); foreach($datetimes as $datetime){ $datetimes_strings[]= $datetime->start_date_and_time();} echo implode(", ",$datetimes_strings); ?> </tr> <?php } break; case EEM_Line_Item::type_sub_line_item: ?> <tr class="item subitem-row"> <td class="item_l subitem"><?php echo $line_item->name(); ?> </td> <?php if ($show_line_item_description) { ?> <td class="item_l"><?php echo $line_item->desc(); ?> </td><?php } ?> <?php if ($line_item->is_percent()) { ?> <td></td> <td class="item_c"><?php echo $line_item->percent(); ?> %</td> <?php } else { //flat discount/surcharge ?> <td></td> <td class="item_c"><?php echo $line_item->unit_price_no_code(); ?> </td> <?php } ?> <td class="item_r"><?php echo $line_item->total_no_code(); ?> </td> </tr> <?php break; case EEM_Line_Item::type_tax: ?> <tr class="item sub-item tax-total"> <td class="item_l"><?php echo $line_item->name(); ?> </td> <?php if ($show_line_item_description) { ?> <td class="item_l"><?php echo $line_item->desc(); ?> </td><?php } ?> <td colspan="2" class="item_c"><?php echo $line_item->percent(); ?> %</td> <td class="item_r"><?php echo $line_item->total_no_code(); ?> </td> </tr><?php break; } }
/** * Checks if this line item, or any of its children, actually has a description. * If none do, then the template can decide to not show any description column * @param EE_Line_Item $line_item * @return boolean */ function check_if_any_line_items_have_a_description(EE_Line_Item $line_item) { if ($line_item->desc()) { return true; } else { foreach ($line_item->children() as $child_line_item) { if ($this->check_if_any_line_items_have_a_description($child_line_item)) { return true; } } //well, if I and my children don't have descriptions, I guess not return false; } }
/** * Calculates the registration's final price, taking into account that they * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes, * and receive a portion of any transaction-wide discounts. * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50 * and brent's final price should be $5.50. * * In order to do this, we basically need to traverse the line item tree calculating * the running totals (just as if we were recalculating the total), but when we identify * regular line items, we need to keep track of their share of the grand total. * Also, we need to keep track of the TAXABLE total for each ticket purchase, so * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total" * when there are non-taxable items; otherwise they would be the same) * * @param EE_Line_Item $line_item * @param array $billable_ticket_quantities array of EE_Ticket IDs and their corresponding quantity that * can be included in price calculations at this moment * @return array keys are line items for tickets IDs and values are their share of the running total, * plus the key 'total', and 'taxable' which also has keys of all the ticket IDs. Eg * array( * 12 => 4.3 * 23 => 8.0 * 'total' => 16.6, * 'taxable' => array( * 12 => 10, * 23 => 4 * ). * So to find which registrations have which final price, we need to find which line item * is theirs, which can be done with * `EEM_Line_Item::instance()->get_line_item_for_registration( $registration );` */ public static function calculate_reg_final_prices_per_line_item(EE_Line_Item $line_item, $billable_ticket_quantities = array()) { //init running grand total if not already if (!isset($running_totals['total'])) { $running_totals['total'] = 0; } if (!isset($running_totals['taxable'])) { $running_totals['taxable'] = array('total' => 0); } foreach ($line_item->children() as $child_line_item) { switch ($child_line_item->type()) { case EEM_Line_Item::type_sub_total: $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item($child_line_item, $billable_ticket_quantities); //combine arrays but preserve numeric keys $running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals); $running_totals['total'] += $running_totals_from_subtotal['total']; $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total']; break; case EEM_Line_Item::type_tax_sub_total: //find how much the taxes percentage is if ($child_line_item->percent() != 0) { $tax_percent_decimal = $child_line_item->percent() / 100; } else { $tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100; } //and apply to all the taxable totals, and add to the pretax totals foreach ($running_totals as $line_item_id => $this_running_total) { //"total" and "taxable" array key is an exception if ($line_item_id === 'taxable') { continue; } $taxable_total = $running_totals['taxable'][$line_item_id]; $running_totals[$line_item_id] += $taxable_total * $tax_percent_decimal; } break; case EEM_Line_Item::type_line_item: // ticket line items or ???? if ($child_line_item->OBJ_type() === 'Ticket') { // kk it's a ticket if (isset($running_totals[$child_line_item->ID()])) { //huh? that shouldn't happen. $running_totals['total'] += $child_line_item->total(); } else { //its not in our running totals yet. great. if ($child_line_item->is_taxable()) { $taxable_amount = $child_line_item->unit_price(); } else { $taxable_amount = 0; } // are we only calculating totals for some tickets? if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) { $quantity = $billable_ticket_quantities[$child_line_item->OBJ_ID()]; $running_totals[$child_line_item->ID()] = $quantity ? $child_line_item->unit_price() : 0; $running_totals['taxable'][$child_line_item->ID()] = $quantity ? $taxable_amount : 0; } else { $quantity = $child_line_item->quantity(); $running_totals[$child_line_item->ID()] = $child_line_item->unit_price(); $running_totals['taxable'][$child_line_item->ID()] = $taxable_amount; } $running_totals['taxable']['total'] += $taxable_amount * $quantity; $running_totals['total'] += $child_line_item->unit_price() * $quantity; } } else { // it's some other type of item added to the cart // it should affect the running totals // basically we want to convert it into a PERCENT modifier. Because // more clearly affect all registration's final price equally $line_items_percent_of_running_total = $running_totals['total'] > 0 ? $child_line_item->total() / $running_totals['total'] + 1 : 1; foreach ($running_totals as $line_item_id => $this_running_total) { //the "taxable" array key is an exception if ($line_item_id === 'taxable') { continue; } // update the running totals // yes this actually even works for the running grand total! $running_totals[$line_item_id] = $line_items_percent_of_running_total * $this_running_total; if ($child_line_item->is_taxable()) { $running_totals['taxable'][$line_item_id] = $line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id]; } } } break; } } return $running_totals; }
/** * Checks line item and children list $txn as their transaction. * Used by E_UnitTestCase_Test::test_new_typical_transaction() * @param EE_Transaction $txn * @param EE_Line_Item $line_item */ private function _ensure_txn_on_line_item_and_children($txn, $line_item) { $this->assertEquals($txn->ID(), $line_item->TXN_ID()); foreach ($line_item->children() as $child_line_item) { $this->_ensure_txn_on_line_item_and_children($txn, $child_line_item); } }
/** * Prints out a representation of the line item tree * @param EE_Line_Item $line_item * @param int $indentation * @return void */ public static function visualize(EE_Line_Item $line_item, $indentation = 0) { echo "\r\n"; for ($i = 0; $i < $indentation; $i++) { echo "-"; } echo $line_item->name() . ": " . $line_item->type() . " \$" . $line_item->total(); if ($line_item->is_taxable()) { echo " taxable"; } if ($line_item->children()) { foreach ($line_item->children() as $child) { self::visualize($child, $indentation + 1); } } }