/** * Return the PayPal form. * * @param IsotopeProductCollection|Order $objOrder The order being places * @param \Module|Checkout $objModule The checkout module instance * * @return string */ public function checkoutForm(IsotopeProductCollection $objOrder, \Module $objModule) { $arrData = array(); $fltDiscount = 0; $i = 0; foreach ($objOrder->getItems() as $objItem) { // Set the active product for insert tags replacement if ($objItem->hasProduct()) { Product::setActive($objItem->getProduct()); } $strConfig = ''; $arrConfig = $objItem->getConfiguration(); if (!empty($arrConfig)) { array_walk($arrConfig, function (&$option) { $option = $option['label'] . ': ' . (string) $option; }); $strConfig = ' (' . implode(', ', $arrConfig) . ')'; } $arrData['item_number_' . ++$i] = $objItem->getSku(); $arrData['item_name_' . $i] = \StringUtil::restoreBasicEntities($objItem->getName() . $strConfig); $arrData['amount_' . $i] = $objItem->getPrice(); $arrData['quantity_' . $i] = $objItem->quantity; } foreach ($objOrder->getSurcharges() as $objSurcharge) { if (!$objSurcharge->addToTotal) { continue; } // PayPal does only support one single discount item if ($objSurcharge->total_price < 0) { $fltDiscount -= $objSurcharge->total_price; continue; } $arrData['item_name_' . ++$i] = $objSurcharge->label; $arrData['amount_' . $i] = $objSurcharge->total_price; } $objTemplate = new \Isotope\Template('iso_payment_paypal'); $objTemplate->setData($this->arrData); $objTemplate->id = $this->id; $objTemplate->action = 'https://www.' . ($this->debug ? 'sandbox.' : '') . 'paypal.com/cgi-bin/webscr'; $objTemplate->invoice = $objOrder->id; $objTemplate->data = array_map('specialchars', $arrData); $objTemplate->discount = $fltDiscount; $objTemplate->address = $objOrder->getBillingAddress(); $objTemplate->currency = $objOrder->currency; $objTemplate->return = \Environment::get('base') . $objModule->generateUrlForStep('complete', $objOrder); $objTemplate->cancel_return = \Environment::get('base') . $objModule->generateUrlForStep('failed'); $objTemplate->notify_url = \Environment::get('base') . 'system/modules/isotope/postsale.php?mod=pay&id=' . $this->id; $objTemplate->headline = specialchars($GLOBALS['TL_LANG']['MSC']['pay_with_redirect'][0]); $objTemplate->message = specialchars($GLOBALS['TL_LANG']['MSC']['pay_with_redirect'][1]); $objTemplate->slabel = specialchars($GLOBALS['TL_LANG']['MSC']['pay_with_redirect'][2]); $objTemplate->noscript = specialchars($GLOBALS['TL_LANG']['MSC']['pay_with_redirect'][3]); return $objTemplate->parse(); }
public static function createForRuleInCollection(RuleModel $objRule, IsotopeProductCollection $objCollection) { // Cart subtotal if ($objRule->minSubtotal > 0 && $objCollection->getSubtotal() < $objRule->minSubtotal || $objRule->maxSubtotal > 0 && $objCollection->getSubtotal() > $objRule->maxSubtotal) { return null; } $arrCollectionItems = $objCollection->getItems(); $blnMatch = false; $blnPercentage = $objRule->isPercentage(); $fltDiscount = $blnPercentage ? $objRule->getPercentage() : 0; $fltTotal = 0; $arrSubtract = array(); $objSurcharge = new static(); $objSurcharge->label = $objRule->getLabel(); $objSurcharge->price = $objRule->getPercentageLabel(); $objSurcharge->total_price = 0; $objSurcharge->tax_class = 0; $objSurcharge->before_tax = true; $objSurcharge->addToTotal = true; // Product or producttype restrictions if ($objRule->productRestrictions != '' && $objRule->productRestrictions != 'none') { $arrLimit = \Database::getInstance()->execute("SELECT object_id FROM tl_iso_rule_restriction WHERE pid={$objRule->id} AND type='{$objRule->productRestrictions}'")->fetchEach('object_id'); if ($objRule->productRestrictions == 'pages' && !empty($arrLimit)) { $arrLimit = \Database::getInstance()->execute("SELECT pid FROM " . \Isotope\Model\ProductCategory::getTable() . " WHERE page_id IN (" . implode(',', $arrLimit) . ")")->fetchEach('pid'); } if ($objRule->quantityMode == 'cart_products' || $objRule->quantityMode == 'cart_items') { $intTotal = 0; foreach ($arrCollectionItems as $objItem) { if (!$objItem->hasProduct()) { continue; } $objProduct = $objItem->getProduct(); if (($objRule->productRestrictions == 'products' || $objRule->productRestrictions == 'variants' || $objRule->productRestrictions == 'pages') && (in_array($objProduct->id, $arrLimit) || $objProduct->pid > 0 && in_array($objProduct->pid, $arrLimit)) || $objRule->productRestrictions == 'producttypes' && in_array($objProduct->type, $arrLimit)) { $intTotal += $objRule->quantityMode == 'cart_items' ? $objItem->quantity : 1; } } } } else { switch ($objRule->quantityMode) { case 'cart_products': $intTotal = $objCollection->countItems(); break; case 'cart_items': $intTotal = $objCollection->sumItemsQuantity(); break; } } foreach ($arrCollectionItems as $objItem) { if (!$objItem->hasProduct()) { continue; } $objProduct = $objItem->getProduct(); // Product restrictions if (($objRule->productRestrictions == 'products' || $objRule->productRestrictions == 'variants' || $objRule->productRestrictions == 'pages') && (!in_array($objProduct->id, $arrLimit) && ($objProduct->pid == 0 || !in_array($objProduct->pid, $arrLimit))) || $objRule->productRestrictions == 'producttypes' && !in_array($objProduct->type, $arrLimit)) { continue; } elseif ($objRule->productRestrictions == 'attribute') { switch ($objRule->attributeCondition) { case 'eq': if (!($objProduct->{$objRule->attributeName} == $objRule->attributeValue)) { continue 2; } break; case 'neq': if (!($objProduct->{$objRule->attributeName} != $objRule->attributeValue)) { continue 2; } break; case 'lt': if (!($objProduct->{$objRule->attributeName} < $objRule->attributeValue)) { continue 2; } break; case 'gt': if (!($objProduct->{$objRule->attributeName} > $objRule->attributeValue)) { continue 2; } break; case 'elt': if (!($objProduct->{$objRule->attributeName} <= $objRule->attributeValue)) { continue 2; } break; case 'egt': if (!($objProduct->{$objRule->attributeName} >= $objRule->attributeValue)) { continue 2; } break; case 'starts': if (stripos($objProduct->{$objRule->attributeName}, $objRule->attributeValue) !== 0) { continue 2; } break; case 'ends': if (strripos($objProduct->{$objRule->attributeName}, $objRule->attributeValue) !== strlen($objProduct->{$objRule->attributeName}) - strlen($objRule->attributeValue)) { continue 2; } break; case 'contains': if (stripos($objProduct->{$objRule->attributeName}, $objRule->attributeValue) === false) { continue 2; } break; default: throw new \Exception('Unknown rule condition "' . $objRule->attributeCondition . '"'); } } // Because we apply to the quantity of only this product, we override $intTotal in every foreach loop // This matches tl_iso_rules.quantityMode="product_quantity" if ($objRule->quantityMode != 'cart_products' && $objRule->quantityMode != 'cart_items') { $intTotal = $objItem->quantity; } // Quantity does not match, do not apply to this product if ($objRule->minItemQuantity > 0 && $objRule->minItemQuantity > $intTotal || $objRule->maxItemQuantity > 0 && $objRule->maxItemQuantity < $intTotal) { continue; } // Apply To switch ($objRule->applyTo) { case 'products': $fltPrice = $blnPercentage ? $objItem->getTotalPrice() / 100 * $fltDiscount : $objRule->discount; $fltPrice = $fltPrice > 0 ? floor($fltPrice * 100) / 100 : ceil($fltPrice * 100) / 100; $objSurcharge->total_price += $fltPrice; $objSurcharge->setAmountForCollectionItem($fltPrice, $objItem); break; case 'items': $fltPrice = ($blnPercentage ? $objItem->getPrice() / 100 * $fltDiscount : $objRule->discount) * $objItem->quantity; $fltPrice = $fltPrice > 0 ? floor($fltPrice * 100) / 100 : ceil($fltPrice * 100) / 100; $objSurcharge->total_price += $fltPrice; $objSurcharge->setAmountForCollectionItem($fltPrice, $objItem); break; case 'subtotal': $blnMatch = true; $objSurcharge->total_price += $objItem->getTotalPrice(); if ($objRule->tax_class == -1) { if ($blnPercentage) { $fltPrice = $objItem->getTotalPrice() / 100 * $fltDiscount; $objSurcharge->setAmountForCollectionItem($fltPrice, $objItem); } else { $arrSubtract[] = $objItem; $fltTotal += (double) $objItem->getTaxFreeTotalPrice(); } } break; } } if ($objRule->applyTo == 'subtotal' && $blnMatch) { // discount total! not related to tax subtraction $fltPrice = $blnPercentage ? $objSurcharge->total_price / 100 * $fltDiscount : $objRule->discount; $objSurcharge->total_price = $fltPrice > 0 ? floor(round($fltPrice * 100, 4)) / 100 : ceil(round($fltPrice * 100, 4)) / 100; $objSurcharge->before_tax = $objRule->tax_class != 0 ? true : false; $objSurcharge->tax_class = $objRule->tax_class > 0 ? $objRule->tax_class : 0; // If fixed price discount with splitted taxes, calculate total amount of discount per taxed product if ($objRule->tax_class == -1 && !$blnPercentage) { foreach ($arrSubtract as $objItem) { $fltPrice = $objRule->discount / 100 * (100 / $fltTotal * $objItem->getTaxFreeTotalPrice()); $objSurcharge->setAmountForCollectionItem($fltPrice, $objItem); } } } return $objSurcharge->total_price == 0 ? null : $objSurcharge; }
/** * Create or add taxes for each collection item * * @param Tax[] $arrTaxes * @param IsotopeProductCollection $objCollection * @param ProductCollectionSurcharge[] $arrSurcharges * @param Address[] $arrAddresses */ private static function addTaxesForItems(array &$arrTaxes, IsotopeProductCollection $objCollection, array $arrSurcharges, array $arrAddresses) { foreach ($objCollection->getItems() as $objItem) { // This should never happen, but we can't calculate it if (!$objItem->hasProduct()) { continue; } $objProduct = $objItem->getProduct(); /** @var \Isotope\Model\TaxClass $objTaxClass */ $objTaxClass = $objProduct->getPrice() ? $objProduct->getPrice()->getRelated('tax_class') : null; // Skip products without tax class if (null === $objTaxClass) { continue; } $arrTaxIds = array(); $fltPrice = $objItem->getTotalPrice(); /** @var \Isotope\Model\ProductCollectionSurcharge $objSurcharge */ foreach ($arrSurcharges as $objSurcharge) { $fltPrice += $objSurcharge->getAmountForCollectionItem($objItem); } /** @var \Isotope\Model\TaxRate $objIncludes */ if (($objIncludes = $objTaxClass->getRelated('includes')) !== null) { if ($objIncludes->isApplicable($fltPrice, $arrAddresses)) { $addToTotal = static::getTaxAddState(false); $total = $addToTotal ? $objIncludes->calculateAmountAddedToPrice($fltPrice) : $objIncludes->calculateAmountIncludedInPrice($fltPrice); $arrTaxIds[] = static::addTax($arrTaxes, $objTaxClass->id . '_' . $objIncludes->id, $objTaxClass->getLabel() ?: $objIncludes->getLabel(), $objIncludes->getAmount(), $objIncludes->isPercentage(), $total, $objTaxClass->applyRoundingIncrement, $addToTotal, false); } } if (($objRates = $objTaxClass->getRelated('rates')) !== null) { /** @var \Isotope\Model\TaxRate $objTaxRate */ foreach ($objRates as $objTaxRate) { if ($objTaxRate->isApplicable($fltPrice, $arrAddresses)) { $addToTotal = static::getTaxAddState(true); $total = $addToTotal ? $objTaxRate->calculateAmountAddedToPrice($fltPrice) : $objTaxRate->calculateAmountIncludedInPrice($fltPrice); $arrTaxIds[] = static::addTax($arrTaxes, $objTaxRate->id, $objTaxRate->getLabel(), $objTaxRate->getAmount(), $objTaxRate->isPercentage(), $total, $objTaxClass->applyRoundingIncrement, $addToTotal, false); if ($objTaxRate->stop) { break; } } } } $strTaxId = implode(',', $arrTaxIds); if ($objItem->tax_id != $strTaxId) { $objCollection->updateItem($objItem, array('tax_id' => $strTaxId)); } foreach ($arrSurcharges as $objSurcharge) { if ($objSurcharge->getAmountForCollectionItem($objItem) > 0) { foreach ($arrTaxIds as $taxId) { $objSurcharge->addTaxNumber($taxId); } } } } }
/** * Copy product collection items from another collection to this one (e.g. Cart to Order) * @param IsotopeProductCollection * @return array */ public function copyItemsFrom(IsotopeProductCollection $objSource) { $this->ensureNotLocked(); $this->updateDatabase(); // Make sure database table has the latest prices $objSource->updateDatabase(); $time = time(); $arrIds = array(); $arrOldItems = $objSource->getItems(); foreach ($arrOldItems as $objOldItem) { // !HOOK: additional functionality when copying product to collection if (isset($GLOBALS['ISO_HOOKS']['copyCollectionItem']) && is_array($GLOBALS['ISO_HOOKS']['copyCollectionItem'])) { foreach ($GLOBALS['ISO_HOOKS']['copyCollectionItem'] as $callback) { $objCallback = \System::importStatic($callback[0]); if ($objCallback->{$callback}[1]($objOldItem, $objSource, $this) === false) { continue; } } } if ($objOldItem->hasProduct() && $this->hasProduct($objOldItem->getProduct())) { $objNewItem = $this->getItemForProduct($objOldItem->getProduct()); $objNewItem->increaseQuantityBy($objOldItem->quantity); } else { $objNewItem = clone $objOldItem; $objNewItem->pid = $this->id; $objNewItem->tstamp = $time; $objNewItem->save(); } $arrIds[$objOldItem->id] = $objNewItem->id; } if (!empty($arrIds)) { $this->tstamp = $time; } // !HOOK: additional functionality when adding product to collection if (isset($GLOBALS['ISO_HOOKS']['copiedCollectionItems']) && is_array($GLOBALS['ISO_HOOKS']['copiedCollectionItems'])) { foreach ($GLOBALS['ISO_HOOKS']['copiedCollectionItems'] as $callback) { $objCallback = \System::importStatic($callback[0]); $objCallback->{$callback}[1]($objSource, $this, $arrIds); } } // Empty cache $this->arrItems = null; $this->arrCache = null; return $arrIds; }
/** * Create ProductCollectionDownload for all product downloads in the given collection * * @param IsotopeProductCollection $objCollection * * @return static[] */ public static function createForProductsInCollection(IsotopeProductCollection $objCollection) { $arrDownloads = array(); $t = Download::getTable(); $time = $objCollection->locked ?: ($objCollection->tstamp ?: time()); foreach ($objCollection->getItems() as $objItem) { if ($objItem->hasProduct()) { $objDownloads = Download::findBy(array("({$t}.pid=? OR {$t}.pid=?)", "{$t}.published='1'"), array($objItem->getProduct()->id, $objItem->getProduct()->pid)); if (null !== $objDownloads) { /** @var Download $objDownload */ foreach ($objDownloads as $objDownload) { $objItemDownload = new static(); $objItemDownload->pid = $objItem->id; $objItemDownload->tstamp = $time; $objItemDownload->download_id = $objDownload->id; if ($objDownload->downloads_allowed > 0) { $objItemDownload->downloads_remaining = $objDownload->downloads_allowed * $objItem->quantity; } $expires = $objDownload->getExpirationTimestamp($time); if (null !== $expires) { $objItemDownload->expires = $expires; } $arrDownloads[] = $objItemDownload; } } } } return $arrDownloads; }
/** * Generate XML data for collection items * * @param IsotopeProductCollection $objCollection * * @return string */ private function getCollectionItemsAsXML(IsotopeProductCollection $objCollection) { $xml = new \DOMDocument(); $articleData = $xml->createElement('article_data'); foreach ($objCollection->getItems() as $objItem) { $article = $xml->createElement('article'); $id = $xml->createAttribute('articleid'); $id->value = $objItem->getSku(); $article->appendChild($id); $quantity = $xml->createAttribute('articlequantity'); $quantity->value = $objItem->quantity; $article->appendChild($quantity); $name = $xml->createAttribute('articlename'); $name->value = $objItem->getName(); $article->appendChild($name); $price = $xml->createAttribute('articleprice'); $price->value = round($objItem->getTaxFreePrice() * 100); $article->appendChild($price); $grossPrice = $xml->createAttribute('articlepricegross'); $grossPrice->value = round($objItem->getPrice() * 100); $article->appendChild($grossPrice); $articleData->appendChild($article); } foreach ($objCollection->getSurcharges() as $objSurcharge) { if ($objSurcharge->total_price > 0 && !$objSurcharge instanceof Shipping && !$objSurcharge instanceof Tax) { $article = $xml->createElement('article'); // Quantity is always 1 because we only have a total price $quantity = $xml->createAttribute('articlequantity'); $quantity->value = 1; $article->appendChild($quantity); $name = $xml->createAttribute('articlename'); $name->value = $objSurcharge->label; $article->appendChild($name); $price = $xml->createAttribute('articleprice'); $price->value = round($objSurcharge->tax_free_total_price * 100); $article->appendChild($price); $grossPrice = $xml->createAttribute('articlepricegross'); $grossPrice->value = round($objSurcharge->total_price * 100); $article->appendChild($grossPrice); $articleData->appendChild($article); } } $xml->appendChild($articleData); return $xml->saveXML($xml->documentElement); }
/** * Build a Hash string based on the shipping address * @param IsotopeProductCollection * @return string */ protected static function makeHash(IsotopeProductCollection $objCollection, $arrExtras = array()) { $strBase = get_called_class(); $strBase .= !empty($arrExtras) ? implode(',', $arrExtras) : ''; $objShippingAddress = $objCollection->getShippingAddress(); $strBase .= $objShippingAddress->street_1; $strBase .= $objShippingAddress->city; $strBase .= $objShippingAddress->subdivision; $strBase .= $objShippingAddress->postal; // Hash the cart too foreach ($objCollection->getItems() as $item) { $strBase .= $item->quantity; $strBase .= $item->id; $strBase .= implode(',', $item->getOptions()); } return md5($strBase); }
/** * Split tax amount amongst collection products * @param IsotopeProductCollection * @param \Model * @param bool */ public function applySplittedTax(IsotopeProductCollection $objCollection, $objSource) { $this->tax_class = 0; $this->before_tax = true; $fltTotal = 0; if (!$objSource->isPercentage()) { $fltTotal = $objCollection->getTaxFreeSubtotal(); if ($fltTotal == 0) { return; } } foreach ($objCollection->getItems() as $objItem) { if ($objSource->isPercentage()) { $fltProductPrice = $objItem->getTotalPrice() / 100 * $objSource->getPercentage(); } else { $fltProductPrice = $this->total_price / 100 * (100 / $fltTotal * $objItem->getTaxFreeTotalPrice()); } $fltProductPrice = $fltProductPrice > 0 ? floor($fltProductPrice * 100) / 100 : ceil($fltProductPrice * 100) / 100; $this->setAmountForCollectionItem($fltProductPrice, $objItem); } }