/** * test_add_ticket_purchase */ public function test_add_ticket_purchase() { // create grand total $total_line_item = EEH_Line_Item::create_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 tickets $this->assertEquals(2, $ticket_line_item->quantity()); $this->assertEquals(33, $ticket_line_item->total()); // confirm subtotal $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($total_line_item); $this->assertEquals(33, $pre_tax_subtotal->total()); // confirm taxes $taxes_subtotal = EEH_Line_Item::get_taxes_subtotal($total_line_item); $this->assertEquals(4.95, $taxes_subtotal->total()); // confirm totals $this->assertEquals(37.95, $total_line_item->total()); // one moar ticket plz $ticket_line_item = EEH_Line_Item::add_ticket_purchase($total_line_item, $ticket); // confirm tickets $this->assertEquals(3, $ticket_line_item->quantity()); $this->assertEquals(49.5, $ticket_line_item->total()); // confirm subtotal $this->assertEquals(49.5, $pre_tax_subtotal->total()); // confirm taxes $this->assertEquals(7.43, $taxes_subtotal->total()); // confirm totals $this->assertEquals(56.93, $total_line_item->total()); // total ticket line items count? should just be one ticket line item $this->assertEquals(1, count(EEH_Line_Item::get_ticket_line_items($total_line_item))); // 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()); $this->assertEquals(69.5, $pre_tax_subtotal->total()); // should be same taxes as before $this->assertEquals(7.43, $taxes_subtotal->total()); $this->assertEquals(76.93000000000001, $total_line_item->total()); // total ticket ticket line items? $this->assertEquals(2, count(EEH_Line_Item::get_ticket_line_items($total_line_item))); }
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(); }
/** * Given the grand total line item and a ticket, finds the event sub-total * line item the ticket's purchase should be added onto * * @access public * @param EE_Line_Item $grand_total the grand total line item * @param EE_Ticket $ticket * @throws \EE_Error * @return EE_Line_Item */ public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket) { $first_datetime = $ticket->first_datetime(); if (!$first_datetime instanceof EE_Datetime) { throw new EE_Error(sprintf(__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'), $ticket->ID())); } $event = $first_datetime->event(); if (!$event instanceof EE_Event) { throw new EE_Error(sprintf(__('The supplied ticket (ID %d) has no event data associated with it.', 'event_espresso'), $ticket->ID())); } $event_line_item = NULL; $found = false; foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) { // default event subtotal, we should only ever find this the first time this method is called if (!$event_line_item->OBJ_ID()) { // let's use this! but first... set the event details EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); $found = true; break; } else { if ($event_line_item->OBJ_ID() === $event->ID()) { // found existing line item for this event in the cart, so break out of loop and use this one $found = true; break; } } } if (!$found) { //there is no event sub-total yet, so add it $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total); // create a new "event" subtotal below that $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event); // and set the event details EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); } return $event_line_item; }
/** * Gets the event line item * @param EE_Line_Item $grand_total * @param EE_Event $event * @return EE_Line_Item for the event subtotal which is a child of $grand_total */ public static function get_event_line_item(EE_Line_Item $grand_total, $event) { /** @type EE_Event $event */ $event = EEM_Event::instance()->ensure_is_obj($event, true); $event_line_item = NULL; $found = false; foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) { // default event subtotal, we should only ever find this the first time this method is called if (!$event_line_item->OBJ_ID()) { // let's use this! but first... set the event details EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); $found = true; break; } else { if ($event_line_item->OBJ_ID() === $event->ID()) { // found existing line item for this event in the cart, so break out of loop and use this one $found = true; break; } } } if (!$found) { //there is no event sub-total yet, so add it $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total); // create a new "event" subtotal below that $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event); // and set the event details EEH_Line_Item::set_event_subtotal_details($event_line_item, $event); } return $event_line_item; }
/** * Gets the total for all the items purchased only * @return float */ public function get_items_total() { //by default, let's make sure we're consistent with the existing line item if ($this->is_total()) { $pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this); if ($pretax_subtotal_li instanceof EE_Line_Item) { return $pretax_subtotal_li->total(); } } $total = 0; foreach ($this->get_items() as $item) { if ($item instanceof EE_Line_Item) { $total += $item->total(); } } return $total; }
/** * @group 4710 * @group current */ public function test_update_txn_based_on_payment__paypal_adds_taxes_and_shipping__twice() { $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(); $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); $tax_in_1st_ipn = 1.8; $ship_in_1st_ipn = 3.0; $p = $this->new_model_obj_with_dependencies('Payment', array('TXN_ID' => $t->ID(), 'STS_ID' => EEM_Payment::status_id_approved, 'PMD_ID' => $ppm->ID(), 'PAY_amount' => $t->total() / 2, 'PAY_details' => array('tax' => "{$tax_in_1st_ipn}", 'mc_shipping' => "{$ship_in_1st_ipn}"))); $p->update_extra_meta(EEG_Paypal_Standard::itemized_payment_option_name, false); //taxes shouldn't have been changed, despite whatever paypal says, because //we didnt send them an itemized total so they cant have calculated taxes right anyways $this->assertNotEquals($tax_in_1st_ipn, $old_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 wasnt updated'); $ppg->update_txn_based_on_payment($p); //check the new tax wasnt changed $this->assertEquals($old_tax_total, $t->tax_total()); $this->assertNotEquals($tax_in_1st_ipn, $t->tax_total()); //taxes shouldnt have changed $pre_tax_total = EEH_Line_Item::get_pre_tax_subtotal($t->total_line_item()); $shipping1_line_item = $pre_tax_total->get_child_line_item('paypal_shipping_' . $t->ID()); $this->assertNull($shipping1_line_item); //ok now let's pretend they made another payment via paypal and added more onto the taxes. //but we only update taxes when paypal received an itemized payment from us, which it didn't $tax_in_2nd_ipn = 1.5; $ship_in_2nd_ipn = 8.0; $p2 = $this->new_model_obj_with_dependencies('Payment', array('TXN_ID' => $t->ID(), 'STS_ID' => EEM_Payment::status_id_approved, 'PMD_ID' => $ppm->ID(), 'PAY_amount' => $t->remaining(), 'PAY_details' => array('tax' => "{$tax_in_2nd_ipn}", 'mc_shipping' => "{$ship_in_2nd_ipn}"))); $p2->update_extra_meta(EEG_Paypal_Standard::itemized_payment_option_name, false); $ppg->update_txn_based_on_payment($p2); //assert that the total tax is now the SUM of both IPN's tax amounts $this->assertEquals($tax_in_2nd_ipn, $t->tax_total()); //verify the old shipping is still there $shipping1_line_item = $pre_tax_total->get_child_line_item('paypal_shipping_' . $t->ID()); $this->assertNull($shipping1_line_item); }
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_pre_tax_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_pre_tax_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()); }