<?php namespace QUI\ERP\Coupons; use QUI; use QUI\Utils\Grid; use QUI\Utils\Security\Orthos; use QUI\ERP\Discount\Handler as DiscountHandler; /** * Class Handler * * Main CouponCode Code handler */ class Handler { /** * Permissions */ const PERMISSION_VIEW = 'quiqqer.couponcode.view'; const PERMISSION_CREATE = 'quiqqer.couponcode.create'; const PERMISSION_EDIT = 'quiqqer.couponcode.edit'; const PERMISSION_DELETE = 'quiqqer.couponcode.delete'; /** * CouponCode runtime cache * * @var CouponCode[] */ protected static $couponCodes = []; /** * Get CouponCode * * @param int $id * @return CouponCode * @throws CouponCodeException */ public static function getCouponCode($id) { if (isset(self::$couponCodes[$id])) { return self::$couponCodes[$id]; } self::$couponCodes[$id] = new CouponCode($id); return self::$couponCodes[$id]; } /** * Get CouponCode by its actual code * * @param string $code * @return CouponCode * * @throws CouponCodeException */ public static function getCouponCodeByCode($code) { $result = QUI::getDataBase()->fetch([ 'select' => [ 'id' ], 'from' => self::getTable(), 'where' => [ 'code' => $code ], 'limit' => 1 ]); if (empty($result)) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.Handler.code_not_found', [ 'code' => $code ] ], 404); } return self::getCouponCode($result[0]['id']); } /** * Create new CouponCode * * @param array $discountIds - IDs of the discounts that are linked to this CouponCode * @param array $settings (optional) - If omitted a random default CouponCode is generated * @return CouponCode * * @throws \Exception */ public static function createCouponCode($discountIds, $settings = []) { $DiscountHandler = DiscountHandler::getInstance(); if (empty($discountIds)) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.Handler.no_discounts_linked' ]); } // check if all given discounts exist foreach ($discountIds as $discountId) { try { $DiscountHandler->getChild($discountId); } catch (\Exception $Exception) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.Handler.discount_error', [ 'discountId' => $discountId, 'error' => $Exception->getMessage() ] ]); } } $Now = new \DateTime(); if (!empty($settings['code'])) { if (self::existsCode($settings['code'])) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.Handler.code_already_exists', [ 'code' => $settings['code'] ] ]); } $code = $settings['code']; } else { $code = CodeGenerator::generate(); } $couponCode = [ 'title' => empty($settings['title']) ? null : $settings['title'], 'createDate' => $Now->format('Y-m-d H:i:s'), 'code' => $code, 'isReusable' => !empty($settings['reusable']), 'discountIds' => json_encode($discountIds) ]; if (!empty($settings['validUntilDate'])) { $ValidUntil = new \DateTime($settings['validUntilDate']); $couponCode['validUntilDate'] = $ValidUntil->format('Y-m-d H:i:s'); } if (!empty($settings['userIds'])) { $couponCode['userIds'] = json_encode($settings['userIds']); } if (!empty($settings['groupIds'])) { $couponCode['groupIds'] = json_encode($settings['groupIds']); } QUI::getDataBase()->insert( self::getTable(), $couponCode ); return self::getCouponCode(QUI::getPDO()->lastInsertId()); } /** * Edit a CouponCode * * @param int $id - CouponCode ID * @param array $discountIds - IDs of the discounts that are linked to this CouponCode * @param array $settings (optional) - If omitted a random default CouponCode is generated * @return CouponCode * * @throws \Exception */ public static function editCouponCode($id, $discountIds, $settings = []) { QUI\Permissions\Permission::checkPermission(self::PERMISSION_EDIT); // check if CouponCode exists $CouponCode = self::getCouponCode($id); // Check settings $DiscountHandler = DiscountHandler::getInstance(); if (empty($discountIds)) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.Handler.no_discounts_linked' ]); } // check if all given discounts exist foreach ($discountIds as $discountId) { try { $DiscountHandler->getChild($discountId); } catch (\Exception $Exception) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.Handler.discount_error', [ 'discountId' => $discountId, 'error' => $Exception->getMessage() ] ]); } } $Now = new \DateTime(); if (!empty($settings['code'])) { if ($CouponCode->getCode() !== $settings['code'] && self::existsCode($settings['code'])) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.Handler.code_already_exists', [ 'code' => $settings['code'] ] ]); } $code = $settings['code']; } else { $code = CodeGenerator::generate(); } $couponCode = [ 'title' => empty($settings['title']) ? null : $settings['title'], 'createDate' => $Now->format('Y-m-d H:i:s'), 'code' => $code, 'isReusable' => !empty($settings['reusable']), 'discountIds' => json_encode($discountIds) ]; if (!empty($settings['validUntilDate'])) { $ValidUntil = new \DateTime($settings['validUntilDate']); $couponCode['validUntilDate'] = $ValidUntil->format('Y-m-d H:i:s'); } else { $couponCode['validUntilDate'] = null; } if (!empty($settings['userIds'])) { $couponCode['userIds'] = json_encode($settings['userIds']); } else { $couponCode['userIds'] = null; } if (!empty($settings['groupIds'])) { $couponCode['groupIds'] = json_encode($settings['groupIds']); } else { $couponCode['groupIds'] = null; } QUI::getDataBase()->update( self::getTable(), $couponCode, [ 'id' => $id ] ); return self::getCouponCode($id); } /** * Search CouponCodes * * @param array $searchParams * @param bool $countOnly (optional) - get result count only [default: false] * @return CouponCode[]|int * @throws CouponCodeException */ public static function search($searchParams, $countOnly = false) { $couponCodes = []; $Grid = new Grid($searchParams); $gridParams = $Grid->parseDBParams($searchParams); $binds = []; $where = []; if ($countOnly) { $sql = "SELECT COUNT(*)"; } else { $sql = "SELECT id"; } $sql .= " FROM `".self::getTable()."`"; if (!empty($searchParams['search'])) { $searchColumns = [ 'id', 'code', 'email' ]; $whereOr = []; foreach ($searchColumns as $searchColumn) { $whereOr[] = '`'.$searchColumn.'` LIKE :search'; } if (!empty($whereOr)) { $where[] = '('.implode(' OR ', $whereOr).')'; $binds['search'] = [ 'value' => '%'.$searchParams['search'].'%', 'type' => \PDO::PARAM_STR ]; } } // build WHERE query string if (!empty($where)) { $sql .= " WHERE ".implode(" AND ", $where); } // ORDER if (!empty($searchParams['sortOn']) ) { $sortOn = Orthos::clear($searchParams['sortOn']); $order = "ORDER BY ".$sortOn; if (isset($searchParams['sortBy']) && !empty($searchParams['sortBy']) ) { $order .= " ".Orthos::clear($searchParams['sortBy']); } else { $order .= " ASC"; } $sql .= " ".$order; } else { $sql .= " ORDER BY id DESC"; } // LIMIT if (!empty($gridParams['limit']) && !$countOnly ) { $sql .= " LIMIT ".$gridParams['limit']; } else { if (!$countOnly) { $sql .= " LIMIT ".(int)20; } } $Stmt = QUI::getPDO()->prepare($sql); // bind search values foreach ($binds as $var => $bind) { $Stmt->bindValue(':'.$var, $bind['value'], $bind['type']); } try { $Stmt->execute(); $result = $Stmt->fetchAll(\PDO::FETCH_ASSOC); } catch (\Exception $Exception) { QUI\System\Log::addError( self::class.' :: search() -> '.$Exception->getMessage() ); return []; } if ($countOnly) { return (int)current(current($result)); } foreach ($result as $row) { $couponCodes[] = self::getCouponCode($row['id']); } return $couponCodes; } /** * Check if an invite code already eixsts * * @param string $code * @return bool */ public static function existsCode($code) { $result = QUI::getDataBase()->fetch([ 'select' => 'id', 'from' => self::getTable(), 'where' => [ 'code' => $code ], 'limit' => 1 ]); return !empty($result); } /** * Get Registration site * * @return QUI\Projects\Site|false */ public static function getRegistrationSite() { $Conf = QUI::getPackage('quiqqer/coupons')->getConfig(); $regSite = $Conf->get('settings', 'registrationSite'); if (empty($regSite)) { return false; } try { return QUI\Projects\Site\Utils::getSiteByLink($regSite); } catch (\Exception $Exception) { return false; } } /** * Deletes all CouponCodes that are expired * * @param int $days (optional) - Delete expired Codes that are older than X days [default: delete all] * @return void * * @throws \Exception */ public static function deleteExpiredCouponCodes($days = null) { $Now = new \DateTime(); $where = [ 'validUntilDate' => [ 'type' => '<=', 'value' => $Now->format('Y-m-d H:i:s') ] ]; if (!is_null($days)) { $days = (int)$days; $OldDate = new \DateTime(); $OldDate->sub(new \DateInterval('P'.$days.'D')); $where['validUntilDate'] = [ 'type' => '<=', 'value' => $OldDate->format('Y-m-d H:i:s') ]; } QUI::getDataBase()->delete( self::getTable(), $where ); } /** * Deletes all CouponCodes that have been redeemed * * @param int $days (optional) - Delete redeemed Codes that are older than X days [default: delete all] * @return void * * @throws \Exception */ public static function deleteRedeemedCouponCodes($days = null) { $where = [ 'useDate' => [ 'type' => 'NOT', 'value' => null ] ]; if (!is_null($days)) { $days = (int)$days; $OldDate = new \DateTime(); $OldDate->sub(new \DateInterval('P'.$days.'D')); $where['useDate'] = [ 'type' => '<=', 'value' => $OldDate->format('Y-m-d H:i:s') ]; } QUI::getDataBase()->delete( self::getTable(), $where ); } /** * Get CouponCode table * * @return string */ public static function getTable() { return QUI::getDBTableName('quiqqer_coupons'); } }