public function testSetSingleAddress() { $this->order->addProduct($this->products[0], 1); $this->controller->setOrder($this->order); $this->controller->setMultiAddress(); $shipment1 = Shipment::getNewInstance($this->order); $shipment1->save(); $shipment2 = Shipment::getNewInstance($this->order); $shipment2->save(); $this->order->addProduct($this->products[0], 1, true, $shipment1); $this->order->addProduct($this->products[1], 2, true, $shipment2); $this->order->save(); $order = $this->reloadOrder($this->order); $this->assertEqual($order->getShipments()->size(), 2); $this->assertEqual(count($order->getOrderedItems()), 3); $this->controller->setOrder($order = $this->reloadOrder($this->order)); $response = $this->controller->setSingleAddress(); $order = $this->reloadOrder($order); $this->assertIsA($response, 'ActionRedirectResponse'); $this->assertEqual($order->isMultiAddress->get(), '0'); $this->assertEqual($order->getShipments()->size(), 1); $this->assertEqual(count($order->getOrderedItems()), 2); }
/** * Update product quantities */ public function update() { // TOS if ($this->isTosInCartPage()) { $this->session->set('tos', $this->request->get('tos')); } // coupon code if ($this->request->get('coupon')) { $code = $this->request->get('coupon'); if ($condition = DiscountCondition::getInstanceByCoupon($code)) { if (!$this->order->hasCoupon($code)) { $coupon = OrderCoupon::getNewInstance($this->order, $code); $coupon->save(); $this->order->getCoupons(true); if ($this->order->hasCoupon($code)) { $this->setMessage($this->makeText('_coupon_added', array($code))); } } } else { $this->setErrorMessage($this->makeText('_coupon_not_found', array($code))); } $this->order->getCoupons(true); } $this->updateEstimateAddress(); $this->order->loadItemData(); $validator = $this->buildCartValidator($this->order, $this->getItemOptions()); if (!$validator->isValid()) { return new ActionRedirectResponse('order', 'index'); } $this->order->loadRequestData($this->request); foreach ($this->order->getOrderedItems() as $item) { if ($this->request->isValueSet('item_' . $item->getID())) { foreach ($item->getProduct()->getOptions(true) as $option) { $this->modifyItemOption($item, $option, $this->request, $this->getFormFieldName($item, $option)); } $item->save(); $this->order->updateCount($item, $this->request->get('item_' . $item->getID(), 0)); } } if ($this->order->isMultiAddress->get()) { $addresses = $this->user->getShippingAddressSet(); $this->order->getShipments(); foreach ($this->order->getOrderedItems() as $item) { if ($addressId = $this->request->get('address_' . $item->getID())) { if (!$item->shipment->get() || !$item->shipment->get()->shippingAddress->get() || $item->shipment->get()->shippingAddress->get()->getID() != $addressId) { foreach ($this->order->getShipments() as $shipment) { if ($shipment->shippingAddress->get() && $shipment->shippingAddress->get()->getID() == $addressId) { if (!$item->shipment->get() || $item->shipment->get()->getID() != $shipment->getID()) { if ($item->shipment->get()) { $item->shipment->get()->removeItem($item); } $shipment->addItem($item); break; } } $shipment = null; } if (!isset($shipment) || !$shipment) { $address = ActiveRecordModel::getInstanceById('UserAddress', $addressId, true); $shipment = Shipment::getNewInstance($this->order); $shipment->shippingAddress->set($address); $shipment->save(); $this->order->addShipment($shipment); $shipment->addItem($item); } $item->save(); } } if ($item->shipment->get()) { $item->shipment->get()->shippingAmount->set(0); $item->shipment->get()->shippingServiceData->set(null); $item->shipment->get()->save(); } } } $this->order->mergeItems(); SessionOrder::save($this->order); // proceed with the checkout if ($this->request->get('proceed')) { return new ActionRedirectResponse('checkout', 'index'); } // redirect to payment gateway if ($url = $this->request->get('redirect')) { return new RedirectResponse($url); } return new ActionRedirectResponse('order', 'index', array('query' => 'return=' . $this->request->get('return'))); }
/** * @role update */ public function create() { $order = CustomerOrder::getInstanceByID((int) $this->request->get('orderID'), true, array('BillingAddress', 'ShippingAddress')); $shipment = Shipment::getNewInstance($order); $history = new OrderHistory($order, $this->user); $response = $this->save($shipment); $history->saveLog(); return $response; }
public function getDownloadShipment($createNew = true) { // look for a shipment that only contains downloadable items foreach ($this->getShipments() as $shipment) { if (!$shipment->isShippable()) { return $shipment; } } // look for an empty shipment foreach ($this->getShipments() as $shipment) { if (!count($shipment->getItems())) { return $shipment; } } if ($createNew) { $shipment = Shipment::getNewInstance($this); $shipment->save(true); $this->shipments->add($shipment); return $shipment; } }
public function create() { $request = $this->getRequest(); $query = $request->get('query'); if (strlen($query)) { $products = $this->getProductsFromSearchQuery($query); } else { $products = new ARSet(); $products->add(Product::getInstanceById((int) $this->request->get('productID'), true)); } $saveResponse = array('errors' => array(), 'items' => array()); $composite = new CompositeJSONResponse(); $order = CustomerOrder::getInstanceByID((int) $this->request->get('orderID'), true); $order->loadAll(); foreach ($products as $product) { if ($product->isDownloadable()) { $shipment = $order->getDownloadShipment(); } else { if ((int) $this->request->get('shipmentID')) { $shipment = Shipment::getInstanceById('Shipment', (int) $this->request->get('shipmentID'), true, array('Order' => 'CustomerOrder', 'ShippingService', 'ShippingAddress' => 'UserAddress', 'Currency')); } } if (empty($shipment)) { $shipment = $order->getShipments()->get(0); } if (!$shipment) { $shipment = Shipment::getNewInstance($order); } if (!$shipment->order->get()) { $shipment->order->set($order); } $history = new OrderHistory($order, $this->user); $existingItem = false; foreach ($shipment->getItems() as $item) { if ($item->getProduct() === $product) { if (!$product->getOptions(true)) { $existingItem = $item; } break; } } if ($existingItem) { $item = $existingItem; if ($product->isDownloadable()) { return new JSONResponse(false, 'failure', $this->translate('_downloadable_item_already_exists_in_this_order')); } else { $item->count->set($item->count->get() + 1); } } else { $currency = $shipment->getCurrency(); $item = OrderedItem::getNewInstance($order, $product); $item->count->set(1); $item->price->set($currency->round($item->reduceBaseTaxes($product->getPrice($currency->getID())))); $order->addItem($item); $shipment->addItem($item); $shipment->save(); } $resp = $this->save($item, $shipment, $existingItem ? true : false); if (array_key_exists('errors', $resp)) { $saveResponse['errors'] = array_merge($saveResponse['errors'], $resp['errors']); } else { if (array_key_exists('item', $resp)) { $saveResponse['items'][] = $resp['item']; } } } // for each product if (count($saveResponse['errors']) == 0) { unset($saveResponse['errors']); } if (isset($saveResponse['errors'])) { $response = new JSONResponse(array('errors' => $validator->getErrorList()), 'failure', $this->translate('_unable_to_update_items_quantity')); } else { $response = new JSONResponse($saveResponse, 'success', $this->translate('_item_has_been_successfuly_saved')); } $composite->addResponse('data', $response, $this, 'create'); $ids = array(); foreach ($saveResponse['items'] as $item) { $ids[] = $item['ID']; } $composite->addAction('html', 'backend.orderedItem', 'items'); $this->request->set('item_ids', implode(',', $ids)); $history->saveLog(); return $composite; }
public function testGenerateInvoices_longScenario() { // configruation $config = ActiveRecordModel::getApplication()->getConfig(); $config->set('RECURRING_BILLING_GENERATE_INVOICE', 3); $config->set('RECURRING_BILLING_PAYMENT_DUE_DATE_DAYS', 7); $config->save(); // other orders (as some information noise). for ($i = 0; $i < 3; $i++) { $order = CustomerOrder::getNewInstance($this->user); $product = $this->products[$i]; $product->save(); $order->addProduct($product, $i + 2); $order->save(); $order->finalize(); } //.. // order with multiple shipments, multiple recurring periods (everyting multiple) $period1 = $this->createRecurringProductPeriod($this->products[0], 15, RecurringProductPeriod::TYPE_PERIOD_DAY, null); // infinite rebill count $period2 = $this->createRecurringProductPeriod($this->products[1], 15, RecurringProductPeriod::TYPE_PERIOD_DAY, 2); $period3 = $this->createRecurringProductPeriod($this->products[2], 1, RecurringProductPeriod::TYPE_PERIOD_WEEK, 3); // every week, for 3 weeks $order = CustomerOrder::getNewInstance($this->user); $order->isMultiAddress->set(true); $order->save(true); $shipment1 = Shipment::getNewInstance($order); $shipment1->save(); $shipment2 = Shipment::getNewInstance($order); $shipment2->save(); $shipment3 = Shipment::getNewInstance($order); $shipment3->save(); $order->addShipment($shipment1); $order->addShipment($shipment2); $order->addShipment($shipment3); list($orderedItem1, $recurringItem1) = $this->addRecurringProduct($order, $this->products[0], 1, $period1, 33.01, 50.01, $shipment1); list($orderedItem2, $recurringItem2) = $this->addRecurringProduct($order, $this->products[1], 3, $period2, 44.02, 100.02, $shipment2); list($orderedItem3, $recurringItem3) = $this->addRecurringProduct($order, $this->products[2], 2, $period3, 0, 60.03, $shipment3); $order->save(); $order->startDate->set('2010-01-01 00:00:00'); $order->finalize(); // $order->dateCompleted->set('2010-01-01 00:00:01'); $order->save(); $this->assertInvoices(array($order->getID()), array('count' => 1, 'totalPrice' => array(33.01 + 3 * 44.02, 'Setup price should be ' . (33.01 + 3 * 44.02) . ' (33.01 + 3 * 44.02)'), 'rebillsLeft' => array(-1))); // ** TIMELINE ** // 2010-01-01 order created; period1 starts; period2 starts; period3 starts // 2010-01-02 // 2010-01-03 // 2010-01-04 // 2010-01-05 3 days before period3 start // 2010-01-06 // 2010-01-07 // 2010-01-08 period3 starts // 2010-01-09 // 2010-01-10 // 2010-01-11 // 2010-01-12 3 days before period3 start // 2010-01-13 3 days before period1 starts; 3 days before period2 starts // 2010-01-14 // 2010-01-15 period3 starts // 2010-01-16 period 1 starts; period2 starts // 2010-01-17 // 2010-01-18 // 2010-01-19 3 days before period3 start // 2010-01-20 // 2010-01-21 // 2010-01-22 period3 starts // 2010-01-23 // 2010-01-24 // 2010-01-25 // 2010-01-26 3 days before period3 start // 2010-01-27 3 days before period1 starts; 3 days before period2 starts // 2010-01-28 // 2010-01-29 period3 starts // 2010-01-30 // 2010-01-31 period 1 starts; period2 starts // 2010-02-01 // 2010-02-02 3 days before period3 should start, but it has run out of rebills, should happen nothing. // 2010-02-03 // 2010-02-04 // 2010-02-05 period 3 rebill count expires // 2010-02-06 // 2010-02-07 // 2010-02-08 // 2010-02-09 // 2010-02-10 // 2010-02-11 // 2010-02-12 // 2010-02-13 // 2010-02-14 // 2010-02-15 // 2010-02-16 // 2010-02-17 // 2010-02-18 // 2010-02-19 // 2010-02-20 // 2010-02-21 // 2010-02-22 // 2010-02-23 // 2010-02-24 // 2010-02-25 // 2010-02-26 // 2010-02-27 // 2010-02-28 // 2010-03-01 $this->assertEquals(3, $order->getShipments()->size()); $this->assertEquals(-1, $order->rebillsLeft->get()); // from 2009-12-01 to 2010-01-04 there should be nothing to generate (see timeline above) $this->assertIntervalHasNoInvoicesToGenerate('2009-12-01', '2010-01-04'); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-01-05'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(1, 'Should generate invoice for period3 (3 days before period start)'), 'periods' => array(array('2010-01-08', '2010-01-14')), 'totalPrice' => 60.03 * 2, 'rebillsLeft' => array(2))); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-01-05'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(0, 'When called second time for same date should not generate more invoices'))); $this->assertIntervalHasNoInvoicesToGenerate('2010-01-06', '2010-01-11'); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-01-12'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(1, '3 days before period 3 starts, should generate invoice'), 'periods' => array(array('2010-01-15', '2010-01-21')), 'rebillsLeft' => array(1))); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-01-13'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(2, 'Should generate invoices for period1 and period2'), 'orderedItemCount' => array(2, 'Should have 2 ordered items - one for product 1 other for product 2'), 'totalPrice' => array(50.01 + 100.02 * 3, 'total price should be 50.01 + 100.02 * 3'), 'periods' => array(array('2010-01-16', '2010-01-30'), array('2010-01-16', '2010-01-30')))); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-01-13'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(0, 'When called second time for same date should not generate more invoices'))); $this->assertIntervalHasNoInvoicesToGenerate('2010-01-14', '2010-01-18'); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-01-13'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(0, 'When called second time for same date should not generate more invoices'))); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-01-19'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(1, '3 days before period 3 starts, should generate invoice'), 'periods' => array(array('2010-01-22', '2010-01-28')), 'rebillsLeft' => array(0))); $generatedOrdersIDs = CustomerOrder::generateRecurringInvoices('2010-02-02'); $this->assertInvoices($generatedOrdersIDs, array('count' => array(0, 'period 3 expired'))); }
public static function generateRecurringInvoices($forDate = null) { $generatedInvoiceIDs = array(); // return only ids for generated invoice, because 1000+ CustomerOrder instances are expensive if ($forDate == null) { $ts = time(); } else { if (is_numeric($forDate)) { $ts = $forDate; } else { $ts = strtotime($forDate); } } $count = 0; $orderIDrecurringItemIDMapping = self::getRecurringPeriodsEndingTodayArray($ts); $orders = self::getRecurringOrders($orderIDrecurringItemIDMapping); if ($orders->size() == 0) { return $generatedInvoiceIDs; } /* echo "date: ", date('Y-m-d', $ts), "\n"; print_r($orderIDrecurringItemIDMapping); echo "\n"; */ $config = self::getApplication()->getConfig(); $daysDue = $config->get('RECURRING_BILLING_PAYMENT_DUE_DATE_DAYS'); $daysBefore = $config->get('RECURRING_BILLING_GENERATE_INVOICE'); foreach ($orders as $order) { $mainOrder = $order; $foundOrderID = $order->getID(); // order found with sql, this id is used in customerOrder+recurringItem mapping as key. $parent = $order->parentID->get(); if ($parent) { $mainOrder = $parent; } else { // echo 'generating from first invoice (could contain multiple plans merged!)'; } $mainOrderID = $mainOrder->getID(); $mainOrder->isRecurring->set(true); $mainOrder->save(); // group by recurring period type, length, (rebill count?) $groupedRecurringItems = array(); foreach ($mainOrder->getOrderedItems() as $item) { $recurringItem = RecurringItem::getInstanceByOrderedItem($item); $recurringItemID = $recurringItem->getID(); if (isset($orderIDrecurringItemIDMapping[$foundOrderID]) && in_array($recurringItemID, $orderIDrecurringItemIDMapping[$foundOrderID])) { $rpp = $recurringItem->recurringID->get(); $groupedRecurringItems[sprintf('%s_%s_%s', $rpp->periodType->get(), $rpp->periodLength->get(), $rpp->rebillCount->get() === null ? 'NULL' : $rpp->rebillCount->get())][] = array('item' => $item, 'recurringItem' => $recurringItem); } } foreach ($groupedRecurringItems as $itemGroups) { $recurringItemIDs = array(); $newOrder = clone $mainOrder; $newOrder->parentID->set($mainOrder); $newOrder->isFinalized->set(false); $newOrder->invoiceNumber->set($newOrder->getCalculatedRecurringInvoiceNumber()); $newOrder->dateDue->set(date('Y-m-d H:i:s', strtotime('+' . ($daysBefore + $daysDue) . ' day', $ts))); $newOrder->isRecurring->set(true); $newOrder->save(true); // order must be saved for setting recurringItem lastInvoiceID. // !! don't save order while it dont have any item or order will be deleted. foreach ($newOrder->getOrderedItems() as $itemToRemove) { $newOrder->removeItem($itemToRemove); } foreach ($newOrder->getShipments() as $shipment) { //echo '{Shipment ID:'.$shipment->getID().'}'; $newOrder->removeShipment($shipment); } foreach ($itemGroups as $itemGroup) { $item = $itemGroup['item']; $recurringItem = $itemGroup['recurringItem']; $newOrderShipment = Shipment::getNewInstance($newOrder); // ~ should have multiple shipments? $recurringItem->saveLastInvoice($newOrder); $periodLength = $recurringItem->periodLength->get(); $periodType = $recurringItem->periodType->get(); $recurringItemIDs[] = $recurringItem->getID(); // collect IDs for batch processedRebillCount update. $clone = clone $item; $clone->recurringParentID->set($item); $clone->shipmentID->set(null); $clone->save(); $newOrderShipment->addItem($clone); $newOrder->addShipment($newOrderShipment); } if (count($itemGroups) > 0) { $newOrder->updateStartAndEndDates($order, array_map(array(__CLASS__, '_filterRecurringItems'), $itemGroups)); } if ($newOrder->isExistingRecord()) { $generatedInvoiceIDs[] = $newOrder->getID(); } if (count($recurringItemIDs)) { // nedd to be done before CustomerOrder::finalize() because finalize() also updates CustomerOrder.rebillsLeft field. // therefore can't really do batch for all, need to do for every order. still, can reuse batch method. RecurringItem::batchIncreaseProcessedRebillCount($recurringItemIDs); } $newOrder->save(); $newOrder->finalize(); } } return $generatedInvoiceIDs; }
/** * Import or update an ordered product */ protected function set_OrderedItem_sku($instance, $value, $record, CsvImportProfile $profile) { if (!$value) { return; } $product = Product::getInstanceBySKU($value); if (!$product) { return; } $items = $instance->getItemsByProduct($product); // create initial shipment if (!$instance->getShipments()->size()) { $shipment = Shipment::getNewInstance($instance); $shipment->save(); } // any particular shipment? $shipment = $item = null; if ($profile->isColumnSet('OrderedItem.shipment')) { // internal indexes are 0-based, but the import references are 1-based $shipmentNo = $this->getColumnValue($record, $profile, 'OrderedItem.shipment') - 1; if (is_numeric($this->getColumnValue($record, $profile, 'OrderedItem.shipment'))) { foreach ($instance->getShipments() as $key => $shipment) { if ($key == $shipmentNo) { break; } $shipment = null; } // create a new shipment if (!$shipment) { $shipment = Shipment::getNewInstance($instance); $shipment->save(); } foreach ($items as $item) { if ($item->shipment->get() == $shipment) { break; } unset($item); } } } if (!$item) { $item = array_shift($items); } if (!$item) { $count = $this->getColumnValue($record, $profile, 'OrderedItem.count'); $item = OrderedItem::getNewInstance($instance, $product, max(1, $count)); $instance->addItem($item); } if ($profile->isColumnSet('OrderedItem.count')) { $count = $this->getColumnValue($record, $profile, 'OrderedItem.count'); $item->count->set(max(1, $count)); } if ($profile->isColumnSet('OrderedItem.price')) { $item->price->set($this->getColumnValue($record, $profile, 'OrderedItem.price')); } if (!$shipment) { $shipment = $instance->getShipments()->get(0); } $item->shipment->set($shipment); $item->save(); $instance->finalize(array('customPrice' => true, 'allowRefinalize' => true)); }
public function testTaxAmountChange() { $tax = Tax::getNewInstance('GST'); $tax->save(); // shipment delivery zone $zone = DeliveryZone::getNewInstance(); $zone->name->set('Canada'); $zone->isEnabled->set(true); $zone->save(); $country = DeliveryZoneCountry::getNewInstance($zone, 'US'); $country->save(); // taxes TaxRate::getNewInstance($zone, $tax, 10)->save(); $this->order->save(true); $shipment = Shipment::getNewInstance($this->order); $shipment->save(); $this->order->addShipment($shipment); $item = $this->order->addProduct($this->products[0], 1, false, $shipment); // $100 $shipment->recalculateAmounts(); $this->order->save(); $this->assertEqual($shipment->taxAmount->get(), 10); $this->order->updateCount($item, 2); $shipment->recalculateAmounts(); $this->assertEqual($shipment->taxAmount->get(), 20); $this->order->save(); $shipment->save(); // there should only be one ShipmentTax instance for this shipment $this->assertEqual($shipment->getRelatedRecordSet('ShipmentTax')->size(), 1); // reload order and add more items ActiveRecord::clearPool(); $order = CustomerOrder::getInstanceByID($this->order->getID(), true); $order->loadAll(); $shipment = $order->getShipments()->get(0); $order->addProduct($this->products[1], 1, false, $shipment); // $200 $shipment->recalculateAmounts(false); $shipment->save(); // @todo: fix failing assertion // 2 ShipmentTax records are created for the same tax type (1st is not deleted, before the 2nd is created) $this->assertEquals(1, $shipment->getRelatedRecordSet('ShipmentTax')->size(), 'expecting one ShipmentTax'); $order->save(); $this->order->finalize(); /* debug echo "\n Order is finalized!\n"; foreach($shipment->getRelatedRecordSet('ShipmentTax') as $item) { echo 'shipment tax id:', $item->getID(), ', type:', $item->type->get(), ', amount:', $item->getAmount(), ' ('. $item->shipmentID->get()->getTaxAmount() .')', ', shipment id:', $item->shipmentID->get()->getID(), ', taxRate id:', $item->taxRateID->get()->getID(), ', taxClass:', implode(';', $item->taxRateID->get()->taxID->get()->name->get()).'('. $item->taxRateID->get()->taxID->get()->getID() .')', ', zone:', $item->taxRateID->get()->deliveryZoneID->get()->name->get(),'(', $item->taxRateID->get()->deliveryZoneID->get()->getID() ,')', // blame canada!! "\n"; } */ $this->assertEquals(1, $order->getShipments()->size(), 'expecting one shipment'); $this->assertEqual(40, $shipment->getRelatedRecordSet('ShipmentTax')->get(0)->shipmentID->get()->getTaxAmount(), 40); // @todo: fix failing assertion $this->assertEquals(1, $shipment->getRelatedRecordSet('ShipmentTax')->size(), 'expecting one ShipmentTax'); $this->assertEqual($shipment->getRelatedRecordSet('ShipmentTax')->get(0)->amount->get(), 40); }