Newer
Older
use QUI\ERP\Discount\Handler as DiscountHandler;
use QUI\ERP\Order\AbstractOrder;
use QUI\ExceptionStack;
use QUI\Interfaces\Users\User;
use function array_key_first;
use function count;
use function current;
use function in_array;
use function is_array;
use function is_null;
protected mixed $code;
protected mixed $userIds = [];
protected mixed $groupIds = [];
* @var array
protected array $discountIds = [];
protected mixed $usages = [];
* @var string|null
protected ?string $title = null;
protected string $maxUsages = Handler::MAX_USAGE_ONCE_PER_USER;
QUI\System\Log::addError($Exception->getMessage());
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.not_found',
[
'id' => $id
]
], 404);
}
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.not_found',
[
$this->id = (int)$data['id'];
$this->code = $data['code'];
$this->usages = json_decode($data['usages'], true) ?? [];
$this->userIds = json_decode($data['userIds'], true) ?? [];
// migrate user ids
foreach ($this->userIds as $k => $userId) {
if (is_numeric($userId)) {
try {
$this->userIds[$k] = QUI::getUsers()->get($userId)->getUUID();
} catch (QUI\Exception) {
}
}
}
$this->groupIds = json_decode($data['groupIds'], true) ?? [];
// migrate user ids
foreach ($this->groupIds as $k => $groupId) {
if (is_numeric($groupId)) {
try {
$this->groupIds[$k] = QUI::getGroups()->get($groupId)->getUUID();
} catch (QUI\Exception) {
}
}
}
if (!empty($data['maxUsages'])) {
$this->maxUsages = $data['maxUsages'];
$this->discountIds = json_decode($data['discountIds'], true) ?? [];
$this->CreateDate = new DateTime($data['createDate']);
$this->ValidUntilDate = new DateTime($data['validUntilDate']);
$this->ValidUntilDate->setTime(23, 59, 59);
{
return $this->id;
}
/**
* @return string
*/
* @return string|null
/**
* Get all discounts associated with this CouponCode
*
* @return QUI\ERP\Discount\Discount[]
*/
$DiscountHandler = DiscountHandler::getInstance();
foreach ($this->discountIds as $discountId) {
try {
$discounts[] = $DiscountHandler->getChild($discountId);
QUI\System\Log::writeDebugException($Exception);
}
}
// @phpstan-ignore-next-line
/**
* @return int[]
*/
public function getUserIds(): array
{
return $this->userIds;
}
/**
* @return int[]
*/
public function getGroupIds(): array
{
return $this->groupIds;
}
* Redeems this CouponCode
*
* Hint: This may invalidate the code for future use
*
* @param User|null $User - The user that redeems the CouponCode [if omitted use Session User]
* @param AbstractOrder|null $Order (optional) - Link redemption to a specific Order
* @throws QUI\Database\Exception
* @throws ExceptionStack
public function redeem(null | QUI\Interfaces\Users\User $User = null, null | AbstractOrder $Order = null): void
$User = QUI::getUserBySession();
}
$this->checkRedemption($User);
'orderPrefixedId' => false
$usage['orderPrefixedId'] = $Order->getPrefixedNumber();
}
$this->usages[] = $usage;
QUI::getEvents()->fireEvent(
'quiqqerCouponsRedeem',
[
* @param QUI\Interfaces\Users\User $User - If omitted, use session user
* @throws CouponCodeException - Thrown if not redeemable by the given User
public function checkRedemption(QUI\Interfaces\Users\User $User): void
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);
$discountError = $Exception->getMessage();
continue;
}
if ($Discount->canUsedBy($User)) {
$discountsValid = true;
break;
}
}
if (!$discountsValid) {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.discount_invalid',
[
'reason' => $discountError
]
]);
} else {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.discounts_invalid'
]);
}
}
// Max usage restrictions
switch ($this->maxUsages) {
case Handler::MAX_USAGE_ONCE_PER_USER:
if ($this->hasUserRedeemed($User)) {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.already_used'
]);
}
break;
case Handler::MAX_USAGE_ONCE:
if (!empty($this->usages)) {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.already_used'
]);
}
break;
}
// Restriction to QUIQQER user(s)
if (in_array($User->getUUID(), $this->userIds)) {
if (
$this->maxUsages !== Handler::MAX_USAGE_UNLIMITED
&& $this->hasUserRedeemed($User)
) {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.already_used'
]);
}
} else {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.user_not_allowed'
]);
}
// Restriction to QUIQQER group(s)
foreach ($this->groupIds as $groupId) {
if ($User->isInGroup($groupId)) {
$userInGroup = true;
break;
}
}
if (
$this->maxUsages !== Handler::MAX_USAGE_UNLIMITED
&& $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 Order can redeem this CouponCode
*
* @param OrderInterface|null $Order
* @throws CouponCodeException
*/
public function checkOrderRedemption(?OrderInterface $Order): void
{
if ($Order === null) {
return;
}
$DiscountHandler = DiscountHandler::getInstance();
$discountsValid = false;
$discountError = false;
foreach ($this->discountIds as $discountId) {
try {
$Discount = $DiscountHandler->getChild($discountId);
$discountError = $Exception->getMessage();
continue;
}
if (method_exists($Discount, 'canUsedInOrder') && $Discount->canUsedInOrder($Order)) {
$discountsValid = true;
break;
}
}
if (!$discountsValid) {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.discount_invalid',
[
'reason' => $discountError
]
]);
} else {
throw new CouponCodeException([
'quiqqer/coupons',
'exception.CouponCode.discounts_invalid'
]);
}
}
}
* @param User|null $User - If omitted, use session user
public function isRedeemable(
null | QUI\Interfaces\Users\User $User = null,
null | OrderInterface $Order = null
): bool {
} catch (CouponCodeException) {
if ($Order) {
try {
$this->checkOrderRedemption($Order);
} catch (CouponCodeException $Exception) {
$Order->addFrontendMessage($Exception->getMessage());
return false;
}
}
public function hasUserRedeemed(QUI\Interfaces\Users\User $User): bool
foreach ($this->usages as $usage) {
if ($usage['userId'] === $userId) {
return true;
}
public function delete(): void
{
Permission::checkPermission(Handler::PERMISSION_DELETE);
try {
QUI::getDataBase()->delete(
Handler::getTable(),
['id' => $this->id]
);
QUI\System\Log::addError($Exception->getMessage());
}

Patrick Müller
committed
// If hidden discount are connected to this coupon -> delete them as well

Patrick Müller
committed
foreach ($this->getDiscounts() as $Discount) {
if (!empty($Discount->getAttribute('hidden'))) {
try {
$Discount->delete();

Patrick Müller
committed
QUI\System\Log::writeException($Exception);
}
}
}
'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,
'title' => $this->getTitle() ?: false,
'isValid' => $this->isValid(),
'maxUsages' => $this->maxUsages,
'discountIds' => $this->discountIds
$data['validUntilDate'] = $ValidUntilDate->format('Y-m-d');
/**
* Checks if this CouponCode is still valid
*
* @return void
*/
protected function checkValidity(): void
{
// Check if the expiration date has been reached
if (!empty($this->ValidUntilDate)) {
if ($Now > $this->ValidUntilDate) {
$this->valid = false;
if ($this->maxUsages === Handler::MAX_USAGE_UNLIMITED) {
return;
}
if ($this->maxUsages === Handler::MAX_USAGE_ONCE && !empty($this->usages)) {
$this->valid = false;
// 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;
}
}
}
/**
* @param QUI\ERP\Order\OrderInProcess $Order
* @throws QUI\Exception
*/
public function addToOrder(QUI\ERP\Order\OrderInProcess $Order): void
{
$coupons = $Order->getDataEntry('quiqqer-coupons');
if (!$coupons) {
return;
}
$calculations = $Order->getArticles()->getCalculations();
$vatArray = $calculations['vatArray'];
$vat = false;
if (count($vatArray) === 1) {
$vat = array_key_first($vatArray);
foreach ($coupons as $coupon) {
/* @var $Coupon CouponCode */
try {
$Coupon = Handler::getCouponCodeByCode($coupon);
} catch (Exception) {
continue;
}
// coupon check
if (!$Coupon->isRedeemable($Order->getCustomer(), $Order)) {
continue;
}
/* @var $Discount QUI\ERP\Discount\Discount */
$discounts = $Coupon->getDiscounts();
foreach ($discounts as $Discount) {
$PriceFactor = $Discount->toPriceFactor(null, $Order->getCustomer());
if ($vat !== false && method_exists($PriceFactor, 'setVat')) {
$PriceFactor->setVat($vat);
}
$PriceFactor->setTitle(
QUI::getLocale()->get('quiqqer/coupons', 'coupon.discount.title', [
'code' => $Coupon->getCode()
])
);
$priceFactors[] = $PriceFactor;
// @todo wenn fest preis (zb 10$), dann eigener produkt typ hinzufügen
$articles[] = new QUI\ERP\Accounting\Articles\Text([
'articleNo' => $Coupon->getCode(),
'title' => $PriceFactor->getTitle(),
'unitPrice' => 0,
'control' => '',
'quantity' => 1,
'customData' => [
* @return boolean
*/
$isInArticles = function ($Article) use ($Order) {
$articles = $Order->getArticles();
continue;
}
$customData = $Entry->getCustomData();
continue;
}
if (!isset($customData['package']) || !isset($customData['code'])) {
continue;
}
return $customData['package'] === 'quiqqer/coupon'
&& $customData['code'] === $code;
}
return false;
};
foreach ($articles as $Article) {
/* @var $PriceFactor QUI\ERP\Accounting\Articles\Text */
if ($isInArticles($Article) === false) {
$Order->addArticle($Article);
}
}
$Order->update();
$Order->addPriceFactors($priceFactors);