<?php namespace QUI\ERP\Coupons; use QUI; use QUI\Permissions\Permission; use QUI\ERP\Discount\Handler as DiscountHandler; /** * Class CouponCode */ class CouponCode { /** * CouponCode ID * * @var int */ protected $id; /** * Actual code * * @var string */ protected $code; /** * IDs of users that this CouponCode is restricted to * * @var int[] */ protected $userIds = []; /** * IDs of groups that this CouponCode is restricted to * * @var int[] */ protected $groupIds = []; /** * IDs of all linked discounts * * @var int[] */ protected $discountIds = []; /** * List of usages of this CouponCode * * @var array */ protected $usages = []; /** * Creation Date * * @var \DateTime */ protected $CreateDate; /** * Date until the CouponCode is valid * * @var \DateTime */ protected $ValidUntilDate = null; /** * CouponCode title * * @var string */ protected $title = null; /** * Flag - Is the CouponCode valid? * * @var bool */ protected $valid = true; /** * Flag - Is the CouponCode reusable? * * @var bool */ protected $reusable; /** * CouponCode constructor. * * @param int $id - Invite Code ID * @throws \QUI\ERP\Coupons\CouponCodeException */ public function __construct($id) { $result = QUI::getDataBase()->fetch([ 'from' => Handler::getTable(), 'where' => [ 'id' => $id ] ]); if (empty($result)) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.not_found', [ 'id' => $id ] ], 404); } $data = current($result); $this->id = (int)$data['id']; $this->code = $data['code']; $this->title = $data['title']; if (!empty($data['usages'])) { $this->usages = json_decode($data['usages'], true); } if (!empty($data['userIds'])) { $this->userIds = json_decode($data['userIds'], true); } if (!empty($data['groupIds'])) { $this->groupIds = json_decode($data['groupIds'], true); } if (!empty($data['isReusable'])) { $this->reusable = true; } if (!empty($data['discountIds'])) { $this->discountIds = json_decode($data['discountIds'], true); } $this->CreateDate = new \DateTime($data['createDate']); if (!empty($data['validUntilDate'])) { $this->ValidUntilDate = new \DateTime($data['validUntilDate']); } $this->checkValidity(); } /** * @return int */ public function getId() { return $this->id; } /** * @return string */ public function getCode() { return $this->code; } /** * @return \DateTime */ public function getCreateDate() { return $this->CreateDate; } /** * Get usage data * * @return array */ public function getUsages() { return $this->usages; } /** * @return \DateTime|null */ public function getValidUntilDate() { return $this->ValidUntilDate; } /** * @return string */ public function getTitle() { return $this->title; } /** * @return int[] */ public function getDiscountIds() { return $this->discountIds; } /** * Get all discounts associated with this CouponCode * * @return QUI\ERP\Discount\Discount[] */ public function getDiscounts() { $discounts = []; $DiscountHandler = DiscountHandler::getInstance(); foreach ($this->discountIds as $discountId) { try { $discounts[] = $DiscountHandler->getChild($discountId); } catch (\Exception $Exception) { QUI\System\Log::writeDebugException($Exception); } } return $discounts; } /** * Redeems this CouponCode * * Hint: This may invalidate the code for future use * * @param QUI\Users\User $User - The user that redeems the CouponCode [if omitted use Session User] * @return void * @throws CouponCodeException * @throws QUI\Exception */ public function redeem($User = null) { if (is_null($User)) { $User = QUI::getUserBySession(); } $this->checkRedemption($User); $Now = new \DateTime(); $this->usages[] = [ 'userId' => $User->getId(), 'date' => $Now->format('Y-m-d H:i:s') ]; QUI::getDataBase()->update( Handler::getTable(), [ 'usages' => json_encode($this->usages) ], [ 'id' => $this->id ] ); $this->checkValidity(); QUI::getEvents()->fireEvent( 'quiqqerCouponsRedeem', [ 'User' => $User, 'CouponCode' => $this ] ); } /** * Check if the given User can redeem this CouponCode * * @param QUI\Users\User $User - If omitted, use session user * @return void * @throws CouponCodeException - Thrown if not redeemable by the given User */ public function checkRedemption($User) { if (!$this->isValid()) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.no_longer_valid' ]); } $DiscountHandler = DiscountHandler::getInstance(); $discountsValid = false; $discountError = false; foreach ($this->discountIds as $discountId) { try { /** @var QUI\ERP\Discount\Discount $Discount */ $Discount = $DiscountHandler->getChild($discountId); } catch (\Exception $Exception) { $discountError = $Exception->getMessage(); continue; } if ($Discount->canUsedBy($User)) { $discountsValid = true; break; } } if (!$discountsValid) { if (count($this->discountIds) === 1) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.discount_invalid', [ 'reason' => $discountError ] ]); } else { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.discounts_invalid' ]); } } if (!empty($this->userIds)) { if (in_array($User->getId(), $this->userIds)) { if (!$this->reusable && $this->hasUserRedeemed($User)) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.already_used' ]); } } else { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.user_not_allowed' ]); } } if (!empty($this->groupIds)) { $userInGroup = false; foreach ($this->groupIds as $groupId) { if ($User->isInGroup($groupId)) { $userInGroup = true; break; } } if ($userInGroup) { if (!$this->reusable && $this->hasUserRedeemed($User)) { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.already_used' ]); } } else { throw new CouponCodeException([ 'quiqqer/coupons', 'exception.CouponCode.user_not_allowed_group' ]); } } } /** * Check if the given User can redeem this CouponCode * * @param QUI\Users\User $User - If omitted, use session user * @return bool */ public function isRedeemable($User = null) { try { $this->checkRedemption($User); } catch (CouponCodeException $Exception) { return false; } return true; } /** * Check if this CouponCode is still valid * * @return bool */ public function isValid() { return $this->valid; } /** * Checks if an CouponCode has been redeemed by a user * * @param QUI\Users\User $User * @return bool */ public function hasUserRedeemed($User) { $userId = $User->getId(); foreach ($this->usages as $usage) { if ($usage['userId'] === $userId) { return true; } } return false; } /** * Permanently delete this CouponCode * * @return void * @throws \QUI\Permissions\Exception */ public function delete() { Permission::checkPermission(Handler::PERMISSION_DELETE); QUI::getDataBase()->delete( Handler::getTable(), [ 'id' => $this->id ] ); } /** * Get CouponCode attributes as array * * @return array */ public function toArray() { $data = [ 'id' => $this->getId(), 'code' => $this->getCode(), 'userIds' => $this->userIds, 'groupIds' => $this->groupIds, 'createDate' => $this->getCreateDate()->format('Y-m-d H:i:s'), 'usages' => $this->usages, 'validUntilDate' => false, 'title' => $this->getTitle() ?: false, 'isValid' => $this->isValid(), 'reusable' => $this->reusable, 'discountIds' => $this->discountIds ]; $ValidUntilDate = $this->getValidUntilDate(); if ($ValidUntilDate) { $data['validUntilDate'] = $ValidUntilDate->format('Y-m-d'); } return $data; } /** * Checks if this CouponCode is still valid * * @return void */ protected function checkValidity() { // Check if the expiration date has been reached if (!empty($this->ValidUntilDate)) { $Now = new \DateTime(); if ($Now > $this->ValidUntilDate) { $this->valid = false; return; } } if ($this->reusable) { return; } // If the CouponCode is restricted to certain users -> Check if all those // users have already redeemed the code if (!empty($this->userIds)) { $usedByAllUsers = true; foreach ($this->userIds as $userId) { foreach ($this->usages as $usage) { if ($userId == $usage['userId']) { continue 2; } } $usedByAllUsers = false; break; } if ($usedByAllUsers) { $this->valid = false; return; } } elseif (!empty($this->usages)) { $this->valid = false; return; } } }