/** * Either saves or return a CartShipping model based * on the user's shopping cart. * * @return CartShipping|null */ public static function getOrCreateCartShipping() { $objShipping = null; // If we have a shipping object already, update it, otherwise create it. if (Yii::app()->shoppingcart->shipping_id !== null) { $objShipping = CartShipping::model()->findByPk(Yii::app()->shoppingcart->shipping_id); } else { $objShipping = new CartShipping(); if ($objShipping->save() === false) { Yii::log("Error saving Cart Shipping:\n" . print_r($objShipping->getErrors(), true), 'error', 'application.' . __CLASS__ . '.' . __FUNCTION__ . '.' . __LINE__); } } return $objShipping; }
/** * Calculates aggregated total amounts * * Iterates over the cart items in the contents of the cart * to calculate aggregated total amounts including the * subtotal, shipping, tax, discounts and grand total * * @since 1.0 * * @return void **/ function totals () { if (!($this->retotal || $this->changed())) return true; $Totals = new CartTotals(); $this->Totals = &$Totals; // Setup discount calculator $Discounts = new CartDiscounts(); // Free shipping until costs are assessed $this->freeshipping = true; // Identify downloadable products $this->downloads(); // If no items are shipped, free shipping is disabled if (!$this->shipped()) $this->freeshipping = false; foreach ($this->contents as $key => $Item) { $Item->retotal(); $Totals->quantity += $Item->quantity; $Totals->subtotal += $Item->total; // Reinitialize item discount amounts $Item->discount = 0; // Item does not have free shipping, // so the cart shouldn't have free shipping if (!$Item->freeshipping) $this->freeshipping = false; } // Calculate Shipping $Shipping = new CartShipping(); if ($this->changed()) { // Only fully recalculate shipping costs // if the cart contents have changed $Totals->shipping = $Shipping->calculate(); // Save the generated shipping options $this->shipping = $Shipping->options(); } else $Totals->shipping = $Shipping->selected(); // Calculate discounts $Totals->discount = $Discounts->calculate(); //$this->promotions(); $Totals->discount = ($Totals->discount > $Totals->subtotal)?$Totals->subtotal:$Totals->discount; // Calculate taxes $Tax = new CartTax(); $Totals->taxrate = $Tax->rate(); $Totals->tax = $Tax->calculate(); // Calculate final totals $Totals->total = roundprice($Totals->subtotal - roundprice($Totals->discount) + $Totals->shipping + $Totals->tax); do_action_ref_array('ecart_cart_retotal',array(&$this->Totals)); $this->changed = false; $this->retotal = false; }
/** * Display a radio button list of shipping options * to the end user and process the chosen option * * @return void */ public function actionShippingOptions() { $this->publishJS('shipping'); $this->publishJS('zippo'); $this->layout = '/layouts/checkout'; $this->checkoutForm = MultiCheckoutForm::loadFromSessionOrNew(); // Check whether the user has selected a shipping option. if (isset($_POST['MultiCheckoutForm'])) { $this->checkoutForm->attributes = $_POST['MultiCheckoutForm']; $this->checkoutForm->hasTaxModeChanged = false; $this->checkoutForm->setScenario('ShippingOptions'); if ($this->checkoutForm->validate() === true) { $this->checkoutForm->saveFormToSession(); // Update the cart shipping in the database. // Update shipping. If in-store pickup was chosen then we need to // ensure the cart shipping values are updated. $objShipping = CartShipping::getOrCreateCartShipping(); if ($objShipping->hasErrors() === false) { $objShipping->updateShipping(); $this->checkoutForm->addErrors($objShipping->getErrors()); } else { $this->checkoutForm->addErrors($objShipping->getErrors()); } $this->checkoutForm->passedScenario = $this->checkoutForm->getScenario(); $this->redirect($this->createUrl('/checkout/final')); } else { Yii::log(sprintf('Validation of the checkout form failed: %s', print_r($this->checkoutForm->getErrors(), true)), 'error', 'application.' . __CLASS__ . '.' . __FUNCTION__); } } // In the case where the destination does not have a defined tax code, // return to the shipping page with an error message. if (Checkout::verifyUserShippingDestination($this->checkoutForm) === false) { Yii::log(sprintf('Shipping destination is invalid: country=%s state=%s postal=%s', $this->checkoutForm->shippingCountryCode, $this->checkoutForm->shippingStateCode, $this->checkoutForm->shippingPostal), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); $this->redirect(array('/checkout/shipping', 'error-destination' => true)); return; } // Update the cart taxes prior to updating the cart scenarios. Yii::app()->shoppingcart->setTaxCodeByCheckoutForm($this->checkoutForm); $arrCartScenario = Shipping::loadCartScenariosFromSession(); // In the case where no shipping options are available, return to the // shipping page with an error message. // TODO: This isn't quite right. If store pickup is the only option, we // also want to display this error because store pickup is not shown // shown on the shipping options screen. See WS-3267. if ($arrCartScenario === null || count($arrCartScenario) === 0) { Yii::log(sprintf('No shipping options available: country=%s state=%s postal=%s', $this->checkoutForm->shippingCountryCode, $this->checkoutForm->shippingStateCode, $this->checkoutForm->shippingPostal), 'info', 'application.' . __CLASS__ . "." . __FUNCTION__); $this->redirect(array('/checkout/shipping', 'error-destination' => true)); return; } // Render the shipping options. // The options themselves are loaded from the session. // The implication here is that before redirecting to this page, ensure // that the shipping options stored in the session are up to date. $this->render('shippingoptions', array('model' => $this->checkoutForm, 'arrCartScenario' => $arrCartScenario, 'error' => $this->formatErrors())); }
/** * This function will run parse an order that we get from Amazon MWS. * It saves orders of the customers to the DB. * @param $response ListOrderItemsResponse Contains the orders from Amazon * Marketplace WebService * @return void */ public function parseListOrders($response) { $checkDate = date("Y-m-d", strtotime($this->amazon_check_time)); $listOrdersResult = $response->getListOrdersResult(); if ($listOrdersResult->isSetOrders()) { $orders = $listOrdersResult->getOrders(); $orderList = $orders->getOrder(); foreach ($orderList as $order) { if ($order->isSetAmazonOrderId()) { $strOrderId = $order->getAmazonOrderId(); Yii::log("Found Amazon Order " . $strOrderId, 'info', 'application.' . __CLASS__ . "." . __FUNCTION__); $objCart = Cart::LoadByIdStr($strOrderId); if (!$objCart instanceof Cart) { //We ignore orders we've already downloaded $objCart = new Cart(); $objCart->id_str = $strOrderId; $objCart->origin = 'amazon'; //We mark this as just a cart, not an order, because we download the items next $objCart->cart_type = CartType::cart; $objOrderTotal = $order->getOrderTotal(); Yii::log("Order total information " . print_r($objOrderTotal, true), 'info', 'application.' . __CLASS__ . "." . __FUNCTION__); $objCart->total = $objOrderTotal->getAmount(); $objCart->currency = $objOrderTotal->getCurrencyCode(); $objCart->status = OrderStatus::Requested; $objCart->datetime_cre = $order->getPurchaseDate(); $objCart->modified = $order->getLastUpdateDate(); if (!$objCart->save()) { Yii::log("Error saving cart " . print_r($objCart->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } //Since email from is Anonymous, we probably will have to create a shell record $objCustomer = Customer::LoadByEmail($order->getBuyerEmail()); if (!$objCustomer) { $customerName = $this->_getCustomerName($order->getBuyerName()); $objCustomer = new Customer(); $objCustomer->email = $order->getBuyerEmail(); $objCustomer->first_name = $customerName['first_name']; $objCustomer->last_name = $customerName['last_name']; $objCustomer->record_type = Customer::EXTERNAL_SHELL_ACCOUNT; $objCustomer->allow_login = Customer::UNAPPROVED_USER; $objCustomer->save(); } $objCart->customer_id = $objCustomer->id; if (!$objCart->save()) { Yii::log("Error saving cart " . print_r($objCart->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } if ($order->isSetShippingAddress()) { $shippingAddress = $order->getShippingAddress(); $countrycode = Country::IdByCode($shippingAddress->getCountryCode()); if ($shippingAddress->isSetStateOrRegion()) { $objState = State::LoadByCode($shippingAddress->getStateOrRegion(), $countrycode); } $customerName = $this->_getCustomerName($shippingAddress->getName()); $config = array('address_label' => 'amazon', 'customer_id' => $objCustomer->id, 'first_name' => $customerName['first_name'], 'last_name' => $customerName['last_name'], 'address1' => $shippingAddress->getAddressLine1(), 'address2' => trim($shippingAddress->getAddressLine2() . " " . $shippingAddress->getAddressLine3()), 'city' => $shippingAddress->getCity(), 'state_id' => $objState->id, 'postal' => $shippingAddress->getPostalCode(), 'country_id' => $countrycode, 'phone' => $shippingAddress->getPhone()); $objCustAddress = CustomerAddress::findOrCreate($config); $objCustomer->default_billing_id = $objCustAddress->id; $objCustomer->default_shipping_id = $objCustAddress->id; $objCustomer->save(); $objCart->shipaddress_id = $objCustAddress->id; $objCart->billaddress_id = $objCustAddress->id; //Amazon doesn't provide billing data, just dupe if (!$objCart->save()) { Yii::log("Error saving cart " . print_r($objCart->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } Yii::log("Looking for destination " . $objState->country_code . " " . $objState->code . " " . $shippingAddress->getPostalCode(), 'info', 'application.' . __CLASS__ . "." . __FUNCTION__); $objDestination = Destination::LoadMatching($objState->country_code, $objState->code, $shippingAddress->getPostalCode()); if ($objDestination === null) { Yii::log("Did not find destination, using default in Web Store ", 'info', 'application.' . __CLASS__ . "." . __FUNCTION__); $objDestination = Destination::getAnyAny(); } $objCart->tax_code_id = $objDestination->taxcode; $objCart->recalculateAndSave(); } if ($order->isSetShipServiceLevel()) { $strShip = $order->getShipServiceLevel(); //If we have a shipping object already, update it, otherwise create it if (isset($objCart->shipping)) { $objShipping = $objCart->shipping; } else { //create $objShipping = new CartShipping(); if (!$objShipping->save()) { Yii::log("Error saving shipping info for cart " . print_r($objShipping->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } } if ($order->isSetShipmentServiceLevelCategory()) { $strShip = $order->getShipmentServiceLevelCategory(); } $objShipping->shipping_module = get_class($this); $objShipping->shipping_data = $strShip; $objShipping->shipping_method = $this->objModule->getConfig('product'); $objShipping->shipping_cost = 0; $objShipping->shipping_sell = 0; $objShipping->save(); $objCart->shipping_id = $objShipping->id; if (!$objCart->save()) { Yii::log("Error saving cart " . print_r($objCart->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } } //Because Amazon comes down with no payment info, just generate one here $objP = new CartPayment(); $objP->payment_method = $this->objModule->getConfig('ls_payment_method'); $objP->payment_module = get_class($this); $objP->payment_data = 'Amazon'; $objP->payment_amount = $objOrderTotal->getAmount(); $objP->datetime_posted = $order->getPurchaseDate(); if (!$objP->save()) { Yii::log("Error saving payment " . print_r($objP->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } $objCart->payment_id = $objP->id; if (!$objCart->save()) { Yii::log("Error saving cart " . print_r($objCart->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } TaskQueue::CreateEvent('integration', get_class($this), 'ListOrderDetails', $objCart->id_str . "," . $checkDate); } } } } }
/** * merchant_calc() * Callback function for merchant calculated shipping and taxes * taxes calculations unimplemented * returns false when it responds, as acknowledgement of merchant calculations is unnecessary * */ function merchant_calc ($XML) { global $Ecart; if ($XML->content('shipping') == 'false') return true; // ack $sessionid = $XML->content('shopping-session'); $Ecart->resession($sessionid); $Ecart->Order = Ecart_buyObject::__new('Order',$Ecart->Order); $Ecart->Order->listeners(); $Shopping = &$Ecart->Shopping; $Order = &$Ecart->Order; // Get new address information on order $shipto = $XML->tag('anonymous-address'); $Order->Shipping->city = $shipto->content('city'); //['city']['CONTENT'] $Order->Shipping->state = $shipto->content('region'); //['region']['CONTENT'] $Order->Shipping->country = $shipto->content('country-code'); //['country-code']['CONTENT'] $Order->Shipping->postcode = $shipto->content('postal-code'); //['postal-code']['CONTENT'] // Calculate shipping options $Shipping = new CartShipping(); $Shipping->calculate(); $options = $Shipping->options(); if (empty($options)) return true; // acknowledge, but don't respond $methods = $XML->attr('method','name'); $address_id = $XML->attr('anonymous-address','id'); $_ = array('<?xml version="1.0" encoding="UTF-8"?>'); $_[] = "<merchant-calculation-results xmlns=\"http://checkout.google.com/schema/2\">"; $_[] = "<results>"; foreach ($options as $option) { if (in_array($option->name, $methods)) { $_[] = '<result shipping-name="'.$option->name.'" address-id="'.$address_id.'">'; $_[] = '<shipping-rate currency="'.$this->settings['currency'].'">'.number_format($option->amount,$this->precision,'.','').'</shipping-rate>'; $_[] = '<shippable>true</shippable>'; $_[] = '</result>'; } } $_[] = "</results>"; $_[] = "</merchant-calculation-results>"; if(ECART_DEBUG) new EcartError(join("\n",$_),'google-merchant-calculation-results',ECART_DEBUG_ERR); $this->response($_); return false; //no ack }
/** * Extract shipping and billing address information, create address book and map to the carts */ protected function actionConvertAddressBook() { $sql = "select * from xlsws_cart where billaddress_id IS NULL and address_bill IS NOT NULL order by id limit 500"; $arrProducts = Yii::app()->db->createCommand($sql)->query(); while (($result = $arrProducts->read()) !== false) { $result['email'] = strtolower($result['email']); //verify that Customer ID really exists in customer table $objCust = Customer::model()->findByPk($result['customer_id']); if (!$objCust instanceof Customer) { $result['customer_id'] = 0; } if (strlen($result['address_bill']) > 0) { $arrAddress = explode("\n", $result['address_bill']); if (count($arrAddress) == 5) { //old format address, should be 6 pieces $arrAddress[5] = $arrAddress[4]; $strSt = $arrAddress[3]; if ($strSt[0] == " ") { //no state on this address $arrAddress[4] = substr($strSt, 1, 100); $arrAddress[3] = ""; } else { $arrSt = explode(" ", $strSt); $arrAddress[3] = $arrSt[0]; $arrAddress[4] = str_replace($arrSt[0] . " ", "", $strSt); } } $objAddress = new CustomerAddress(); if (count($arrAddress) >= 5) { $objCountry = Country::LoadByCode($arrAddress[5]); if ($objCountry) { $objAddress->country_id = $objCountry->id; $objState = State::LoadByCode($arrAddress[3], $objCountry->id); if ($objState) { $objAddress->state_id = $objState->id; } } $objAddress->address1 = $arrAddress[0]; $objAddress->address2 = $arrAddress[1]; $objAddress->city = $arrAddress[2]; $objAddress->postal = $arrAddress[4]; $objAddress->first_name = $result['first_name']; $objAddress->last_name = $result['last_name']; $objAddress->company = $result['company']; $objAddress->phone = $result['phone']; $objAddress->residential = CustomerAddress::RESIDENTIAL; $objAddress->created = $result['datetime_cre']; $objAddress->modified = $result['datetime_cre']; $objAddress->active = 1; if (empty($objAddress->address2)) { $objAddress->address2 = null; } if (empty($objAddress->company)) { $objAddress->company = null; } $blnFound = false; if ($result['customer_id'] > 0) { //See if this is already in our database $objPriorAddress = CustomerAddress::model()->findByAttributes(array('address1' => $objAddress->address1, 'address2' => $objAddress->address2, 'city' => $objAddress->city, 'postal' => $objAddress->postal, 'first_name' => $objAddress->first_name, 'last_name' => $objAddress->last_name, 'company' => $objAddress->company, 'phone' => $objAddress->phone)); if ($objPriorAddress instanceof CustomerAddress) { Yii::app()->db->createCommand("update xlsws_cart set billaddress_id=" . $objPriorAddress->id . " where id=" . $result['id'])->execute(); $blnFound = true; } else { $objAddress->customer_id = $result['customer_id']; } } else { //We need a shell customer record just for the email $objC = Customer::model()->findByAttributes(array('email' => $result['email'])); if ($objC instanceof Customer) { Yii::app()->db->createCommand("UPDATE xlsws_cart set customer_id=" . $objC->id . " where id=" . $result['id'])->execute(); } else { $objC = new Customer(); $objC->record_type = Customer::GUEST; $objC->email = $result['email']; $objC->first_name = $objAddress->first_name; $objC->last_name = $objAddress->last_name; $objC->company = $objAddress->company; if (!$objC->validate()) { $arrErr = $objC->getErrors(); if (isset($arrErr['email'])) { $objC->email = $result['id'] . "*****@*****.**"; } if (!$objC->validate()) { return print_r($objC->getErrors(), true); } } if (!$objC->save()) { Yii::log("Import Error " . print_r($objC->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); return print_r($objC->getErrors(), true); } else { $cid = $objC->id; } Yii::app()->db->createCommand("UPDATE xlsws_cart set customer_id=" . $cid . " where id=" . $result['id'])->execute(); } $result['customer_id'] = $objC->id; $objAddress->customer_id = $result['customer_id']; } if (!$blnFound) { if (!$objAddress->save()) { //We have a corrupt billing address, just blank it out so import goes on Yii::app()->db->createCommand("update xlsws_cart set address_bill=null where id=" . $result['id'])->execute(); } else { $cid = $objAddress->id; Yii::app()->db->createCommand("update xlsws_cart set billaddress_id=" . $cid . " where id=" . $result['id'])->execute(); } } } else { //We have a corrupt billing address, just blank it out so import goes on Yii::app()->db->createCommand("update xlsws_cart set address_bill=null where id=" . $result['id'])->execute(); } $objAddress = new CustomerAddress(); $objCountry = Country::LoadByCode($result['ship_country']); if ($objCountry) { $objAddress->country_id = $objCountry->id; $objState = State::LoadByCode($result['ship_state'], $objCountry->id); if ($objState) { $objAddress->state_id = $objState->id; } } $objAddress->first_name = $result['ship_firstname']; $objAddress->last_name = $result['ship_lastname']; $objAddress->company = $result['ship_company']; $objAddress->address1 = $result['ship_address1']; $objAddress->address2 = $result['ship_address2']; $objAddress->city = $result['ship_city']; $objAddress->postal = $result['ship_zip']; $objAddress->phone = $result['ship_phone']; $objAddress->residential = CustomerAddress::RESIDENTIAL; $objAddress->created = $result['datetime_cre']; $objAddress->modified = $result['datetime_cre']; $objAddress->active = 1; if (empty($objAddress->address2)) { $objAddress->address2 = null; } if (empty($objAddress->company)) { $objAddress->company = null; } $blnFound = false; if ($result['customer_id'] > 0) { //See if this is already in our database $objPriorAddress = CustomerAddress::model()->findByAttributes(array('address1' => $objAddress->address1, 'city' => $objAddress->city, 'postal' => $objAddress->postal, 'first_name' => $objAddress->first_name, 'last_name' => $objAddress->last_name, 'company' => $objAddress->company, 'phone' => $objAddress->phone)); if ($objPriorAddress instanceof CustomerAddress) { Yii::app()->db->createCommand("update xlsws_cart set shipaddress_id=" . $objPriorAddress->id . " where id=" . $result['id'])->execute(); $blnFound = true; } else { $objAddress->customer_id = $result['customer_id']; } } if (!$blnFound) { if (!$objAddress->save()) { Yii::log("Import Error " . print_r($objAddress->getErrors(), true), 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); } else { $cid = $objAddress->id; Yii::app()->db->createCommand("update xlsws_cart set shipaddress_id=" . $cid . " where id=" . $result['id'])->execute(); } } } $objShipping = new CartShipping(); $objShipping->shipping_method = $result['shipping_method']; $objShipping->shipping_module = $result['shipping_module']; $objShipping->shipping_data = $result['shipping_data']; $objShipping->shipping_cost = $result['shipping_cost']; $objShipping->shipping_sell = $result['shipping_sell']; if (!$objShipping->save()) { return print_r($objShipping->getErrors()); } else { $cid = $objShipping->id; } Yii::app()->db->createCommand("update xlsws_cart set shipping_id=" . $cid . " where id=" . $result['id'])->execute(); $objPayment = new CartPayment(); $objPayment->payment_method = $result['payment_method']; $objPayment->payment_module = str_replace(".php", "", $result['payment_module']); $objPayment->payment_data = $result['payment_data']; $objPayment->payment_amount = $result['payment_amount']; $objPayment->datetime_posted = $result['datetime_posted']; if ($result['fk_promo_id'] > 0) { $objPromo = PromoCode::model()->findByPk($result['fk_promo_id']); if ($objPromo) { $objPayment->promocode = $objPromo->code; } } if (!$objPayment->save()) { return print_r($objPayment->getErrors()); } else { $cid = $objPayment->id; } Yii::app()->db->createCommand("update xlsws_cart set payment_id=" . $cid . " where id=" . $result['id'])->execute(); } $results2 = Yii::app()->db->createCommand("select count(*) from xlsws_cart where billaddress_id IS NULL and address_bill IS NOT NULL")->queryScalar(); if ($results2 == 0) { $remain = 8; } else { $remain = 3; } return array('result' => "success", 'makeline' => $remain, 'total' => 50, 'tag' => 'Converting cart addresses, ' . $results2 . ' remaining'); }
/** * This is the method used by the advanced cart and checkout to retrieve * shipping options and their rates for the shipping estimator. * @return string JSON encoded array of shipping options ordered by price. */ public function actionGetShippingRates() { $checkoutForm = MultiCheckoutForm::loadFromSessionOrNew(); // If no CheckoutForm is posted, then the checkoutForm stored in the // session is used. This is completely valid when we want updated // rates, but haven't modified (or perhaps don't know) the shipping // address. if (isset($_POST['CheckoutForm'])) { $checkoutForm->attributes = $_POST['CheckoutForm']; } // Transform the postcode as required by the shipping modules. // TODO Can we move this? $checkoutForm->shippingPostal = strtoupper(str_replace(' ', '', $checkoutForm->shippingPostal)); // Minimal requirements for shipping: just address details. $checkoutForm->scenario = 'MinimalShipping'; if ($checkoutForm->validate() === false) { return $this->renderJSON(array('result' => 'error', 'errors' => $checkoutForm->getErrors())); } try { $arrCartScenario = Shipping::getCartScenarios($checkoutForm); } catch (Exception $e) { return $this->renderJSON(array('result' => 'error', 'errormsg' => $e->getMessage())); } // Get the shipping estimator JavaScript options. $wsShippingEstimatorOptions = WsShippingEstimator::getShippingEstimatorOptions($arrCartScenario, $checkoutForm->shippingProvider, $checkoutForm->shippingPriority, $checkoutForm->shippingCity, $checkoutForm->shippingStateCode, $checkoutForm->shippingCountryCode); $shippingEstimatorMessage = findWhere($wsShippingEstimatorOptions['messages'], array('code' => 'WARN')); if ($shippingEstimatorMessage !== null) { $message = Yii::t('checkout', 'Your previous shipping selection is no longer available. Please choose an available shipping option.'); Yii::app()->user->setFlash('error', $message); } // Save to session. Shipping::saveCartScenariosToSession($arrCartScenario); MultiCheckoutForm::saveToSession($checkoutForm); // Save to the database. $objShipping = CartShipping::getOrCreateCartShipping(); $objShipping->updateShipping(); return $this->renderJSON(array('result' => 'success', 'wsShippingEstimatorOptions' => $wsShippingEstimatorOptions, 'taxModeChangedMessage' => Yii::app()->user->getFlash('taxModeChange', null))); }