public function GetPrice($objTaxCode, $intTaxStatus, $taxExclusive = false) { if ($taxExclusive) { return $this->price; } elseif (_xls_get_conf('TAX_INCLUSIVE_PRICING', '') == '1') { $arrPrice = Tax::calculatePricesWithTax($this->price, $objTaxCode->id, $intTaxStatus); return $arrPrice['fltSellTotalWithTax']; } else { return $this->price; } }
/** * Calculates the tax on an item * @param obj|int $taxCode :: TaxCode or Rowid to apply * @param float [$fltPrice] :: Price to calculate on * @return array([1] => .... [5]=>)) all the tax components */ public function CalculateTax($intTaxCode, $fltPrice = false) { if ($fltPrice === false) { $fltPrice = $this->getPriceValue(); } $arr = Tax::calculatePricesWithTax($fltPrice, $intTaxCode, $this->tax_status_id); $fltTaxedPrice = $arr['fltSellTotalWithTax']; $arrTaxes = $arr['arrTaxValues']; return $arrTaxes; }
/** * Returns an indexed array of hypothetical cart scenarios ordered by the * shipping price of the scenario from lowest to highest. * * TODO: WS-3481 Refactor this to use Cart instead of ShoppingCart. * TODO: WS-3676 Refactor Shipping::getCartScenarios to implicitly modify the cart and the checkoutform * * @param $checkoutForm * @return array Indexed array of cart scenarios where each cart scenario * is an associative array with the following keys: * formattedCartSubtotal - The formatted subtotal of the cart for this * scenario, * formattedCartTax - The formatted amount of tax on the cart, * formattedCartTotal - The formatted total price of the cart, * formattedShippingPrice - The formatted shipping price, * module - The internal module string identifier (xlsws_module.module). * priorityIndex - An index for the shipping priority (unique per provider), * priorityLabel - A label for the shipping priority, * providerId - The xlsws_module.id of the shipping provider, * providerLabel - A label for the shipping provider, * shippingLabel - A label describing the provider and priority, * shippingPrice - The shipping price for this priortity, * shoppingCart - An instance of ShoppingCart with attributes set for * this scenario, * sortOrder - The xlsws_module.sort_order. * cartItems - The individual cartItem objects for the scenario * * Formatted currencies are formatted according to the user's language. * * @throws Exception If $checkoutForm does not contain enough details to * get shipping rates. * @throws Exception If no shipping providers are enabled (via * Shipping::getAvailableShippingProviders). * @throws Exception If no shipping providers are able to provide rates * (via Shipping::addRatesToShippingProviders). */ public static function getCartScenarios($checkoutForm) { $logLevel = 'info'; if (CPropertyValue::ensureBoolean(_xls_get_conf('DEBUG_SHIPPING', false)) === true) { $logLevel = 'error'; } // TODO: This, and the setting of hasTaxModeChanged, should be // refactored out of this method. It would be better if // getCartScenarios did not have side-effects. Yii::app()->shoppingcart->setTaxCodeByCheckoutForm($checkoutForm); // We are going to modify the shopping cart and save the intermediate // values so we need to save the current value. $savedTaxId = Yii::app()->shoppingcart->tax_code_id; $cart = Yii::app()->shoppingcart->getModel(); // The call to setTaxCodeByCheckoutForm() on the shopping cart will call // recalculateAndSave(). That call is going to add taxes on shipping by // calling updateTaxShipping(). The first run will have the correct values. // On later runs, we will have taxes set in the shopping cart and add more // when we call updateTaxShipping(). Plus, we used to also make a call to // recalculateAndSave() while going through the shipping providers. Then we // would call AddTaxes() which would add taxes on top of taxes. $cart->updateTaxExclusive(); $savedStorePickup = $cart->blnStorePickup; // Get the list of shipping modules. $arrShippingProvider = self::getAvailableShippingProviders($checkoutForm); Yii::log('Got shipping modules ' . print_r($arrShippingProvider, true), $logLevel, 'application.' . __CLASS__ . '.' . __FUNCTION__); // Run each shipping module to get the rates. $arrShippingProvider = self::addRatesToShippingProviders($arrShippingProvider); // Compile each shipping providers rates into an array of "cart scenarios". // Each cart scenario is an associative array containing details about // the cart as it would be if a particular shipping option were chosen. $arrCartScenario = array(); // The shopping cart variable has to be set in case we encounter // a case where the arrShippingProvider is empty. $shoppingCart = Yii::app()->shoppingcart->getModel(); $savedStorePickup = false; foreach ($arrShippingProvider as $shippingModuleId => $shippingProvider) { // Since Store Pickup means paying local taxes, set the cart so our // scenarios work out. if ($shippingProvider['component']->IsStorePickup === true) { Yii::app()->shoppingcart->tax_code_id = TaxCode::getDefaultCode(); $cart->blnStorePickup = true; } else { Yii::app()->shoppingcart->tax_code_id = $savedTaxId; $cart->blnStorePickup = false; } // Get the "shipping" product, which may vary from module to module. $strShippingProduct = $shippingProvider['component']->LsProduct; Yii::log('Shipping Product for ' . $shippingProvider['module']->module . ' is ' . $strShippingProduct, $logLevel, 'application.' . __CLASS__ . "." . __FUNCTION__); if (Yii::app()->params['SHIPPING_TAXABLE'] == 1) { // When shipping is taxable we need to find the tax code on the actual shipping product. $objShipProduct = Product::LoadByCode($strShippingProduct); if ($objShipProduct instanceof Product === true) { $intShipProductLsid = $objShipProduct->taxStatus->lsid; } else { // We may not find a shipping product in cloud mode, so // just use -1 which skips statuses. $intShipProductLsid = -1; } } foreach ($shippingProvider['rates'] as $priorityIndex => $priority) { $priorityPrice = $priority['price']; $includeTaxInShippingPrice = false; $shippingTaxValues = array(); if (Yii::app()->params['SHIPPING_TAXABLE'] == '1') { $shippingTaxPrices = Tax::calculatePricesWithTax($priority['price'], Yii::app()->shoppingcart->tax_code_id, $intShipProductLsid); Yii::log("Shipping Taxes retrieved " . print_r($shippingTaxPrices, true), $logLevel, 'application.' . __CLASS__ . "." . __FUNCTION__); $shippingTaxValues = $shippingTaxPrices['arrTaxValues']; if (Yii::app()->params['TAX_INCLUSIVE_PRICING'] == '1') { $includeTaxInShippingPrice = true; } if ($includeTaxInShippingPrice === true) { $priorityPrice = $shippingTaxPrices['fltSellTotalWithTax']; } else { Yii::app()->shoppingcart->AddTaxes($shippingTaxValues); } } $formattedCartTax = _xls_currency(Yii::app()->shoppingcart->TaxTotal); if (Yii::app()->params['TAX_INCLUSIVE_PRICING'] == '1') { // For tax inclusive stores, we never show cart tax. This is because either: // 1. The destination is inside the tax-inclusive region, or // 2. The destination is inside a tax-exclusive region, in // which case it must be set up as 0% tax. $formattedCartTax = ''; } // TODO: Do the _xls_currency() in the formatter rather than here. $arrCartScenario[] = array('formattedCartSubtotal' => _xls_currency(Yii::app()->shoppingcart->subtotal), 'formattedCartTax' => $formattedCartTax, 'formattedCartTax1' => _xls_currency(Yii::app()->shoppingcart->tax1), 'formattedCartTax2' => _xls_currency(Yii::app()->shoppingcart->tax2), 'formattedCartTax3' => _xls_currency(Yii::app()->shoppingcart->tax3), 'formattedCartTax4' => _xls_currency(Yii::app()->shoppingcart->tax4), 'formattedCartTax5' => _xls_currency(Yii::app()->shoppingcart->tax5), 'formattedCartTotal' => _xls_currency($cart->getTotalWithShipping($priorityPrice)), 'cartTax1' => Yii::app()->shoppingcart->tax1, 'cartTax2' => Yii::app()->shoppingcart->tax2, 'cartTax3' => Yii::app()->shoppingcart->tax3, 'cartTax4' => Yii::app()->shoppingcart->tax4, 'cartTax5' => Yii::app()->shoppingcart->tax5, 'formattedShippingPrice' => _xls_currency($priorityPrice), 'module' => $shippingProvider['module']->module, 'priorityIndex' => $priorityIndex, 'priorityLabel' => $priority['label'], 'providerId' => $shippingModuleId, 'providerLabel' => $shippingProvider['component']->Name, 'shippingLabel' => $shippingProvider['component']->Name . ' ' . $priority['label'], 'shippingPrice' => $priority['price'], 'shippingPriceWithTax' => $priorityPrice, 'shippingProduct' => $strShippingProduct, 'cartAttributes' => $cart->attributes, 'cartItems' => $cart->cartItems, 'sortOrder' => $shippingProvider['module']->sort_order); // Remove shipping taxes to accommodate the next shipping priority in the loop. if (Yii::app()->params['SHIPPING_TAXABLE'] == '1' && $includeTaxInShippingPrice === false) { Yii::app()->shoppingcart->SubtractTaxes($shippingTaxValues); } } } // Restore the original storePickup boolean $cart->blnStorePickup = $savedStorePickup; // Restore the original tax code on the cart. Yii::app()->shoppingcart->setTaxCodeId($savedTaxId); // Sort the shipping options based on the price key. usort($arrCartScenario, function ($item1, $item2) { if ($item1['shippingPrice'] == $item2['shippingPrice']) { return 0; } return $item1['shippingPrice'] > $item2['shippingPrice'] ? 1 : -1; }); return $arrCartScenario; }
/** * Add a quote item * * @param string $passkey * @param string $strId * @param int $intProductId * @param float $fltQty * @param string $strDescription * @param double $fltSell * @param double $fltDiscount * @return string */ public function add_quote_item($passkey, $strId, $intProductId, $fltQty, $strDescription, $fltSell, $fltDiscount) { if (!$this->check_passkey($passkey)) { return self::FAIL_AUTH; } $objDocument = Document::LoadByIdStr($strId); if (!$objDocument) { return self::UNKNOWN_ERROR; } $objProduct = Product::model()->findByPk($intProductId); if (!$objProduct instanceof Product) { Yii::log("SOAP ERROR : Skipping Product not found for Adding to Cart (Quote) -> {$intProductId} ", 'error', 'application.' . __CLASS__ . "." . __FUNCTION__); return self::OK; } $strDescription = trim($strDescription); if (empty($strDescription)) { $strDescription = $objProduct->title; } if (_xls_get_conf('TAX_INCLUSIVE_PRICING') == '1') { $arr = Tax::calculatePricesWithTax($fltSell, $objDocument->fk_tax_code_id, $objProduct->tax_status_id); $fltTaxedPrice = $arr['fltSellTotalWithTax']; } else { $fltTaxedPrice = $fltSell; } $retVal = $objDocument->AddSoapProduct($objDocument->id, $objProduct, $fltQty, $strDescription, $fltTaxedPrice, $fltDiscount, CartType::quote); if (!$retVal) { return self::UNKNOWN_ERROR; } return self::OK; }
/** * Update shipping price and shipping tax price on the cart. */ protected function updateTaxShipping() { if (isset($this->shipping->shipping_sell) === false) { return; } if (Yii::app()->params['SHIPPING_TAXABLE'] != '1') { $this->shipping->shipping_sell_taxed = $this->shipping->shipping_sell; $this->shipping->shipping_taxable = 0; $this->shipping->save(); return; } $this->shipping->shipping_taxable = 1; $this->shipping->save(); $objNoTax = TaxCode::GetNoTaxCode(); $intNoTax = 999; if ($objNoTax instanceof TaxCode) { $intNoTax = $objNoTax->lsid; } if (Yii::app()->params['TAX_INCLUSIVE_PRICING'] == '0' && $this->tax_code_id == $intNoTax) { $this->shipping->shipping_sell_taxed = $this->shipping->shipping_sell; $this->shipping->save(); return; } $objShipProduct = Product::LoadByCode($this->shipping->shipping_method); $intTaxStatus = 0; if ($objShipProduct) { $intTaxStatus = $objShipProduct->taxStatus->lsid; } // Check if the tax status is set to no tax for it, if so, make it // default, otherwise leave it alone. if (Yii::app()->getComponent('storepickup')->IsStorePickup && $intTaxStatus) { $objTaxStatus = $objShipProduct->taxStatus; if ($objTaxStatus && $objTaxStatus->IsNoTax()) { $intTaxStatus = 0; } } $nprice_taxes = Tax::calculatePricesWithTax($this->shipping->shipping_sell, $this->tax_code_id, $intTaxStatus); $taxes = $nprice_taxes['arrTaxValues']; if ($this->tax_code_id == $intNoTax) { $this->shipping->shipping_sell_taxed = $this->shipping->shipping_sell; } else { $this->shipping->shipping_sell_taxed = $nprice_taxes['fltSellTotalWithTax']; if (Yii::app()->params['TAX_INCLUSIVE_PRICING'] != '1') { $this->tax1 += $taxes[1]; $this->tax2 += $taxes[2]; $this->tax3 += $taxes[3]; $this->tax4 += $taxes[4]; $this->tax5 += $taxes[5]; } } $this->shipping->shipping_taxable = 1; $this->shipping->save(); }
/** * Transforms an order (actually a Cart model) into an array structure * matching the JSON response of get_order. * * @param Cart $objCart A completed Cart model. * @param string[] $attributeNames An array of attribute names to return in * the array. Dot notation may be used for accessing model relations. * @return array */ protected function orderToArray($objCart, $attributeNames) { // Copy the specified attributes into the array to be returned. $arrOrder = array(); foreach ($attributeNames as $name) { // Get the relation property using dot-notation. $arrOrder[$name] = CHtml::value($objCart, $name); } // Add the tax rates for each cart item. $arrCartItems = array(); foreach ($arrOrder['cartItems'] as $objCartItem) { $pricesWithTax = Tax::calculatePricesWithTax($objCartItem->sell_total, $objCart->tax_code_id, $objCartItem->product->tax_status_id); // Convert the CartItem from an object to an array with the // iterable properties. $arrCartItem = array(); foreach ($objCartItem as $property => $value) { $arrCartItem[$property] = $value; } // Add any non-zero tax rates to the CartItem array. foreach ($pricesWithTax['arrTaxRates'] as $taxKey => $taxRate) { if ($taxRate > 0) { $arrCartItem['tax' . $taxKey . '_rate'] = $taxRate; } } array_push($arrCartItems, $arrCartItem); } $arrOrder['cartItems'] = $arrCartItems; return $arrOrder; }