'Transaction ID is wrong', ]; /** * @var $this */ protected static $_instance; public static function getInstance() { if (null === self::$_instance) { self::$_instance = new App_ECommerce_Cart_Obj(); self::$_instance->setPrimaryKey(self::$_instance->getCartId()); } return self::$_instance; } /** * Return cart id * * @return int */ public function getCartId() { return $this->_getSessionCartId(); } /** * Return cart id for transaction * * @param int $id * @return int|bool */ public function getCartIdByTransaction($id) { return $this->_getTable()->searchBy(['transactionId' => $id], 'id'); } /** * Return cart id from session * * @return int */ protected function _getSessionCartId() { $cartSession = $this->getSessionCartObject(); if (null === $cartSession->id || null !== $this->getTransactionId($cartSession->id)) { $auth = App\User\Auth::getInstance(); $options = []; if ($auth->hasIdentity()) { $userId = $auth->getData('id'); if ($cartId = $this->getRecentUserCartId($userId)) { $cartSession->id = $cartId; return $cartSession->id; } $options['userId'] = $userId; } $cartSession->id = $this->create($options); } return $cartSession->id; } private function getRecentUserCartId($userId) { $select = $this->_db->select(); $select->from($this->_getPair('Cart'), 'id'); $select->where('`userId` = ?', $userId, Qs_Db::INT_TYPE); $select->where('`transactionId` IS NULL'); $select->order('id DESC'); return $this->_db->fetchOne($select); } /** * Return cart object from session * * @return Qs_Session_Namespace */ public function getSessionCartObject() { return new Qs_Session_Namespace($this->_sessionNamespace); } public function getList($assocMode = false) { if (empty($this->_filter) && null !== Qs_Array::get(self::$_dataCache, 'list[' . intval($assocMode) . ']')) { return self::$_dataCache['list'][$assocMode]; } if (true === $assocMode) { $list = $this->_db->fetchAssoc($this->getListSelect()); } else { $list = $this->getListStatement()->fetchAll(); } $this->_prepareList($list); if (empty($this->_filter) && $list) { self::$_dataCache['list'][$assocMode] = $list; } return $list; } protected function _clearSelect() { if (array_key_exists('list', self::$_dataCache)) { unset(self::$_dataCache['list']); } return parent::_clearSelect(); } /** * Update item quantity * * @param int $itemId * @param int $qty * @param float $price * @param float $tax * @return bool * @throws Qs_Exception */ public function updateItemQty($itemId, $qty, $price = null, $tax = null) { if (!filter_var($itemId, FILTER_VALIDATE_INT) || !filter_var($qty, FILTER_VALIDATE_INT)) { return false; } $itemData = $this->_getTable('CartItem')->searchBy(['cartId' => $this->getPrimaryKey(), 'id' => $itemId]); if (false == $itemData) { return false; } if (in_array(ItemObjFactory::CAPABILITY_CHANGE_QTY, ItemObjFactory::getItemCapabilities($itemData['cartItemType']))) { if ($price === null || $tax === null) { $itemObj = ItemObjFactory::itemDataFactory($itemData); if ($price === null) { $price = $itemObj->getData('price'); } if ($tax === null) { $tax = $itemObj->getData('tax'); } } $data = [ 'quantity' => $qty, 'price' => $price, 'tax' => $tax, ]; if ($this->_getTable('CartItem')->updateByKey($data, $itemId)) { // clear list select to have updated data in list $this->_clearSelect(); return true; } } return 0; } /** * Update transactionId field * * @param int $id * @return bool|Exception * @throws Qs_Exception */ public function updateTransactionId($id) { try { if (!empty($id)) { $this->_getTable()->updateByKey(['transactionId' => $id], $this->getPrimaryKey()); } else { throw new Qs_Exception($this->_exceptionMap[self::EXCEPTION_TRANSACTION_ID_IS_WRONG]); } } catch (Exception $e) { return $e; } return true; } /** * Method will clean transactionId field * * @param int $id * @return bool|Exception * @throws Qs_Exception */ public function cancelTransaction($id) { try { if (!empty($id)) { $this->_getTable()->update(['transactionId' => null], $this->_db->quoteInto('`transactionId` = ?', $id, Qs_Db::INT_TYPE) ); } else { throw new Qs_Exception($this->_exceptionMap[self::EXCEPTION_TRANSACTION_ID_IS_WRONG]); } } catch (Exception $e) { return $e; } return true; } public function setAccessCode($transactionId) { if (!empty($transactionId)) { $accessCode = App_ECommerce_Order_GuestView_Obj::generateAccessCode($transactionId); return $this->_getTable()->updateByKey(['accessCode' => $accessCode], $this->getPrimaryKey()); } return false; } public function getRowPrice($itemId) { return Zend_Locale_Math::round($this->getRowData($itemId, 'price'), 2); } /** * Return total price for one cart item * * @param int $itemId * @return string */ public function getRowTotal($itemId) { return Zend_Locale_Math::round($this->getRowData($itemId, 'rowTotal'), 2); } public function getRowData($itemId, $field = null) { $select = clone $this->getListSelect(); $select->where('`CartItem`.`id` = ?', $itemId, Qs_Db::INT_TYPE); $list = $this->_db->fetchAll($select); if (!empty($list)) { $this->_prepareList($list); $row = reset($list); if (null !== $field) { return (array_key_exists($field, $row)) ? $row[$field] : null; } return $row; } return false; } protected function _prepareList(&$list) { parent::_prepareList($list); $taxPercent = (float) App_Settings_Obj::get('orderPriceTax'); $subtotal = $this->getSubtotal(); foreach ($list as &$row) { $itemObj = ItemObjFactory::itemDataFactory($row); $row = $itemObj->prepareCartItemData(); //calculating tax if promo code assigned to one item if ($row['promoValue'] > 0 && $subtotal >= $row['promoMinOrderValue']) { require_once 'lib/Smarty/app_plugins/modifier.money.php'; $row['promoDescription'] = '-'; if ($row['promoType'] == App_ECommerce_Promo_Admin_View::PROMO_TYPE_PERCENT) { $promoPrice = Zend_Locale_Math::Mul($row['rowTotal'], $row['promoValue'], 4); $promoPrice = Zend_Locale_Math::Div($promoPrice, 100, 4); $row['promoDescription'] .= smarty_modifier_money($row['promoValue'], 2, true) . '% '; } else { $promoPrice = $row['promoValue']; $row['promoDescription'] .= '$' . smarty_modifier_money($row['promoValue']) . ' '; } $row['promoDescription'] .= 'Promo Code Discount'; if (-1 != Zend_Locale_Math::Comp($promoPrice, $row['rowTotal'], 4)) { $promoPrice = Zend_Locale_Math::Sub($row['rowTotal'], self::MIN_SUBTOTAL_VAL, 4); } if ($row['promoType'] != App_ECommerce_Promo_Admin_View::PROMO_TYPE_PERCENT) { $row['promoValue'] = Zend_Locale_Math::round($promoPrice, 2); } $row['promoValuePrice'] = Zend_Locale_Math::round($promoPrice, 2); $row['rowTotal'] = Zend_Locale_Math::Sub($row['rowTotal'], $promoPrice, 4); if ($row['tax'] > 0) { $row['rowTax'] = Zend_Locale_Math::Mul($row['rowTotal'], $taxPercent, 4); $row['rowTax'] = Zend_Locale_Math::Div($row['rowTax'], 100, 4); } } } return $this; } /** * Clear session cart object * * @return App_ECommerce_Cart_Obj */ public function clearSessionCartObject() { $this->getSessionCartObject()->unsetAll(); return $this; } /** * Merge cart for loged in and not loged in users * * @return App_ECommerce_Cart_Obj */ public function mergeCart() { /** @var Qs_Doc $doc */ $doc = Zend_Registry::get('doc'); $userId = $this->getData('userId'); $auth = $doc->getAuth(); if ($auth->hasIdentity()) { $userCartId = $this->getRecentUserCartId($auth->getData('id')); if ($userCartId) { $this->_mergeCartItems($userCartId); $this->getSessionCartObject()->id = $userCartId; } else if (empty($userId)) { $this->update(['userId' => $auth->getData('id')]); } } return $this; } protected function _mergeCartItems($userCartId) { $currentItems = $this->getList(); $userItems = $this->setPrimaryKey($userCartId)->_clearSelect()->getList(); $preparedUserItems = []; foreach ($userItems as $userItem) { $preparedUserItems[$userItem['productId']] = $userItem; } foreach ($currentItems as $item) { if (array_key_exists($item['productId'], $preparedUserItems)) { $quantity = bcadd($item['quantity'], $preparedUserItems[$item['productId']]['quantity']); $this->updateItemQty($preparedUserItems[$item['productId']]['id'], $quantity); } else { $this->addItem($item); } $this->removeItem($item['id']); } return $this; } /** * Return tax price * * @return float|int */ public function getTax() { $items = (array) $this->getList(); $tax = 0; foreach ($items as $item) { $tax = Zend_Locale_Math::Add($tax, $item['rowTax'], 4); } return ((float) $tax > 0 ? $tax : 0); } /** * Validate cart items * * @return bool */ public function validateCartItems() { $this->_clearErrors(); $list = $this->getList(); $result = true; $itemClasses = []; foreach ($list as $idx => $row) { $itemObj = ItemObjFactory::itemDataFactory($row); $itemObj->beforeValidateCart(); $itemClasses[$idx] = $itemObj; } foreach ($list as $idx => $row) { /** @var App_ECommerce_Cart_ItemInterface $itemObj */ $itemObj = $itemClasses[$idx]; if (false === $itemObj->validateCartItem()) { $this->_addError($itemObj->getErrors(), $row['id']); $result = false; continue; } } return $result; } protected function _addError($message, $cartItemId = null) { if (is_string($message)) { $message = [$message]; } if ($cartItemId) { if (!isset($this->_errors[$cartItemId])) { $this->_errors[$cartItemId] = []; } $this->_errors[$cartItemId] = array_merge($this->_errors[$cartItemId], $message); } else { $this->_errors = array_merge($this->_errors, $message); } return $this; } protected function _getFromDbSelect($primaryKey) { $select = parent::_getFromDbSelect($primaryKey); $select->joinLeft( $this->_getPair('CartItemPromo'), '`CartItemPromo`.`cartId` = `' . $this->_tableAlias . '`.`id`', [ 'promoCodeProduct' => 'productId', 'promoCode' => 'code', 'promoCodeValue' => 'value', 'promoCodeType' => 'type', 'promoCodeMinOrderValue' => 'minOrderValue', ] ); $select->joinLeft( $this->_getPair('CartItemGiftCard'), '`CartItemGiftCard`.`cartId` = `' . $this->_tableAlias . '`.`id`', ['giftCardCode' => 'cardCode', 'giftCardValue' => 'cardValue'] ); return $select; } /** * @param $code * @return bool|string return true on success or error code otherwise */ public function applyPromoCode($code) { if ($this->hasPromo()) { return App_ECommerce_Checkout_Abstract_View::ERROR_PROMO_HAS_OTHER; } $promoObj = new App_ECommerce_Promo_Admin_Obj(); $promo = $promoObj->getDataByCode($code); if (empty($promo)) { return App_ECommerce_Checkout_Abstract_View::ERROR_PROMO_INCORRECT; } $errorCode = null; $currentDate = strtotime(date('Y-m-d')); if (strtotime($promo['startDate']) > $currentDate) { $errorCode = App_ECommerce_Checkout_Abstract_View::ERROR_PROMO_INCORRECT; } else if ($promo['hasNoExpirationDate'] == 'n' && strtotime($promo['endDate']) < $currentDate) { $errorCode = App_ECommerce_Checkout_Abstract_View::ERROR_PROMO_INCORRECT; } else if (($total = $this->getSubtotal()) < $promo['minOrderValue']) { $errorCode = App_ECommerce_Checkout_Abstract_View::ERROR_PROMO_LOW_AMOUNT; } else if ( $promo['isRelated'] == 'y' && $promo['productId'] && !$this->getItem(['productId' => $promo['productId']]) ) { $errorCode = App_ECommerce_Checkout_Abstract_View::ERROR_PROMO_NO_PRODUCT; } else if (!$this->_addPromo($promo)) { $errorCode = App_ECommerce_Checkout_Abstract_View::ERROR_OTHER; } return (null == $errorCode) ? true : $errorCode; } public function removePromoCode() { $where = $this->_db->quoteInto('`cartId` = ?', $this->getPrimaryKey(), Qs_Db::INT_TYPE); return (bool) $this->_getTable('CartItemPromo')->delete($where); } public function hasPromo() { return (bool) $this->getData('promoCode'); } protected function _addPromo($data) { $data['promoId'] = $data['id']; $data['cartId'] = $this->getPrimaryKey(); unset($data['added'], $data['changed'], $data['id']); if ($data['isRelated'] == 'y' && !$this->getItem(['productId' => $data['productId']])) { return false; } else if ($data['isRelated'] == 'n') { $data['productId'] = 0; } return (bool) $this->_getTable('CartItemPromo')->insert($data); } public function applyGiftCard($code) { $giftCardObj = new App_ECommerce_GiftCard_Admin_Obj(); $result = $giftCardObj->getDataByCode($code); if ($result) { if ($this->hasGiftCard() || strtotime($result['expirationDate'] < strtotime(date('Y-m-d'))) || $this->getData('userId') != $result['userId'] || $result['statusId'] == App_ECommerce_GiftCard_Admin_View::CARD_STATUS_REDEEMED ) { $result = false; } else { $result = $this->_addGiftCard($result); } } return (bool) $result; } public function removeGiftCard() { $where = $this->_db->quoteInto('`cartId` = ?', $this->getCartId(), Qs_Db::INT_TYPE); return (bool) $this->_getTable('CartItemGiftCard')->delete($where); } public function hasGiftCard() { return (bool) $this->getData('giftCardCode'); } protected function _addGiftCard($data) { $data['giftCardId'] = $data['id']; $data['cartId'] = $this->getPrimaryKey(); unset($data['added'], $data['changed'], $data['id']); return (bool) $this->_getTable('CartItemGiftCard')->insert($data); } /** * Return array with discounts values * @param string|null $type discount type (promo|giftCard) * @return array */ public function getDiscounts($type = null) { $discounts = ['promo' => 0, 'giftCard' => 0]; if ($this->hasPromo() || $this->hasGiftCard()) { $subtotal = $this->getSubtotal(); $cart = $this->getData(); if ($this->hasPromo() && !$cart['promoCodeProduct'] && $this->getSubtotal() >= $cart['promoCodeMinOrderValue'] ) { $discounts['promo'] = $this->_getPromoDiscount($cart, $subtotal); $subtotal = Zend_Locale_Math::Sub($subtotal, $discounts['promo'], 2); } if ($this->hasGiftCard()) { $discounts['giftCard'] = $this->_getGiftCardDiscount($cart, $subtotal); } } return (null !== $type) ? Qs_Array::get($discounts, $type) : $discounts; } /** * @param array $cart * @param float $subtotal * @return float */ protected function _getPromoDiscount(array $cart, $subtotal) { $discount = $cart['promoCodeValue']; if ($cart['promoCodeType'] == App_ECommerce_Promo_Admin_View::PROMO_TYPE_PERCENT) { $result = Zend_Locale_Math::Div(Zend_Locale_Math::Mul($this->getTotal(), $discount, 4), 100, 4); } else { $result = $discount; } if (-1 != Zend_Locale_Math::Comp($result, $subtotal, 2)) { $result = Zend_Locale_Math::Sub($subtotal, App_ECommerce_Cart_Obj::MIN_SUBTOTAL_VAL, 4); } return $result; } /** * @param array $cart * @param float $subtotal * @return float */ protected function _getGiftCardDiscount(array $cart, $subtotal) { if (0 == $subtotal) { return 0; } $giftCardObj = new App_ECommerce_GiftCard_Admin_Obj(); $cardData = $giftCardObj->getDataByCode($cart['giftCardCode']); if (!empty($cardData) && $cardData['statusId'] != App_ECommerce_GiftCard_Admin_View::CARD_STATUS_REDEEMED ) { $result = $cart['giftCardValue']; if (-1 != Zend_Locale_Math::Comp($result, $subtotal, 2)) { $result = Zend_Locale_Math::Sub($subtotal, App_ECommerce_Cart_Obj::MIN_SUBTOTAL_VAL, 2); } } else { $result = 0; } return $result; } /** * it may be just single service product at shopping cart * * @return $this */ public function addServiceProduct() { if ($this->isServiceProductInside()) { return $this; } $serviceObj = new \App\ECommerce\ServiceProduct\Obj(); $this->addItem($serviceObj->prepareCartItemData()); return $this; } }