Newer
Older
<?php
/**
* This file contains QUI\ERP\Discount\Discount
*/
use QUI\ERP\Products\Interfaces\PriceFactorInterface;
use QUI\ERP\Products\Interfaces\PriceFactorWithVatInterface;
use QUI\ERP\Products\Utils\PriceFactor;
use function array_key_exists;
use function explode;
use function implode;
use function is_array;
use function is_numeric;
use function is_string;
use function strtotime;
use function time;
/**
* Class Discount
* @package QUI\ERP\Discount
*/
class Discount extends QUI\CRUD\Child
{
*/
public function __construct($id, Handler $Factory)
{
parent::__construct($id, $Factory);
// attributes
switch ($this->getAttribute('discount_type')) {
case Handler::DISCOUNT_TYPE_CURRENCY:
case Handler::DISCOUNT_TYPE_PERCENT:
break;
default:
$this->setAttribute('discount_type', Handler::DISCOUNT_TYPE_PERCENT);
break;
}
if ($this->getAttribute('consider_vat') === false) {
$this->setAttribute('consider_vat', 'auto');
}
$scope = (int)$this->getAttribute('scope');
switch ($scope) {
case Handler::DISCOUNT_SCOPE_EVERY_PRODUCT:
case Handler::DISCOUNT_SCOPE_TOTAL:
case Handler::DISCOUNT_SCOPE_UNIQUE:
$this->setAttribute('scope', $scope);
break;
default:
$this->setAttribute('scope', Handler::DISCOUNT_SCOPE_TOTAL);
}
// cleanup user group save
$cleanup = QUI\Utils\ArrayHelper::cleanup($this->getAttribute('user_groups'));
if (!empty($cleanup)) {
}
$this->setAttribute('user_groups', $cleanup);
// cleanup product(s)
$cleanup = QUI\Utils\ArrayHelper::cleanup($this->getAttribute('articles'));
if (!empty($cleanup)) {
}
$this->setAttribute('articles', $cleanup);
// cleanup user group save
$cleanup = QUI\Utils\ArrayHelper::cleanup($this->getAttribute('user_groups'));
if (!empty($cleanup)) {
}
$this->setAttribute('user_groups', $cleanup);
// cleanup product(s)
$cleanup = QUI\Utils\ArrayHelper::cleanup($this->getAttribute('articles'));
if (!empty($cleanup)) {
}
$this->setAttribute('articles', $cleanup);
$this->Events->addEvent('onDeleteBegin', function () {
Permission::checkPermission('quiqqer.areas.area.delete');
});
$this->Events->addEvent('onDeleteEnd', function () {
QUI\Translator::delete(
'quiqqer/discount',
);
});
$this->Events->addEvent('onSaveBegin', function () {
Permission::checkPermission('quiqqer.areas.area.edit');
if (
$this->getAttribute('date_from')
&& !Orthos::checkMySqlDatetimeSyntax($this->getAttribute('date_from'))
) {
if (
$this->getAttribute('date_until')
&& !Orthos::checkMySqlDatetimeSyntax($this->getAttribute('date_until'))
) {
$purchaseQuantityFrom = $this->getAttribute('purchase_quantity_from');
$purchaseQuantityUntil = $this->getAttribute('purchase_quantity_until');
$purchaseValueFrom = $this->getAttribute('purchase_value_from');
$purchaseValueUntil = $this->getAttribute('purchase_value_until');
if ($purchaseQuantityFrom !== '' && ($purchaseQuantityFrom === false || $purchaseQuantityFrom < 0)) {
'quiqqer/discount',
'exception.discount.purchase_quantity_from.wrong'
if ($purchaseQuantityUntil !== '' && ($purchaseQuantityUntil === false || $purchaseQuantityUntil < 0)) {
'quiqqer/discount',
'exception.discount.purchase_quantity_until.wrong'
if ($purchaseValueFrom !== '' && ($purchaseValueFrom === false || $purchaseValueFrom < 0)) {
'quiqqer/discount',
'exception.discount.purchase_value_from.wrong'
if ($purchaseValueUntil !== '' && ($purchaseValueUntil === false || $purchaseValueUntil < 0)) {
'quiqqer/discount',
'exception.discount.purchase_value_until.wrong'
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// default nulls
$attributes = [
'discount',
'usage_type',
'discount_type',
'date_from',
'date_until',
'price_calculation_basis',
'purchase_quantity_from',
'purchase_quantity_until',
'purchase_value_from',
'purchase_value_until',
'areas',
'articles',
'categories',
'user_groups',
'combined',
'priority',
'scope',
'lastSumDiscount',
'lastProductDiscount',
];
foreach ($attributes as $attribute) {
if ($this->getAttribute($attribute) === '') {
$this->setAttribute($attribute, null);
}
}
public function setAttribute(string $name, mixed $value): void
if ($name === 'lastSumDiscount' && empty($value)) {
$value = null;
if ($name === 'lastProductDiscount' && empty($value)) {
$value = null;
if (
$name === 'scope' ||
$name === 'discount_type' ||
$name === 'usage_type'

Henning Leutz
committed
) {

Henning Leutz
committed
}
parent::setAttribute($name, $value);
* Return the discount title
*
* @param null|QUI\Locale $Locale - optional, locale object
public function getTitle(null | QUI\Locale $Locale = null): string
if (!$Locale) {
$Locale = QUI::getLocale();
}
return $Locale->get(
);
}
/**
* Return the discount status
*
* @return boolean
*/
return (bool)$this->getAttribute('active');
/**
* Is the discount combinable with another discount?
*
* @param Discount $Discount
* @return bool
*/
public function canCombinedWith(Discount $Discount): bool
{
$combine = $this->getAttribute('combine');
if (empty($combine)) {
return false;
}
$combine = implode(',', $combine);
if (in_array($Discount->getId(), (array)$combine)) {
return true;
}
return false;
}
/**
* is the user allowed to use the discount
*
public function canUsedBy(QUI\Interfaces\Users\User $User): bool
if ($this->isActive() === false) {
return false;
}
// usage definitions / limits
$dateFrom = $this->getAttribute('date_from');
$dateUntil = $this->getAttribute('date_until');
if ($dateFrom && strtotime($dateFrom) > $now) {
return false;
}
if ($dateUntil && strtotime($dateUntil) < $now) {
return false;
}
// assignment
$userGroupValue = $this->getAttribute('user_groups');
$areasValue = $this->getAttribute('areas');
// if groups and areas are empty, everybody is allowed
if (empty($userGroupValue) && empty($areasValue)) {
return true;
}
// not in area
if (!empty($areasValue) && !AreaUtils::isUserInAreas($User, $areasValue)) {
return false;
}
$userGroups = QUI\Utils\UserGroups::parseUsersGroupsString(
$discountGroups = $userGroups['groups'];
// user checking
foreach ($discountUsers as $uid) {
if ($User->getId() == $uid) {
return true;
}
if ($User->getUUID() == $uid) {
return true;
}
}
// group checking
$groupsOfUser = $User->getGroups();
/* @var $Group QUI\Groups\Group */
foreach ($discountGroups as $gid) {
foreach ($groupsOfUser as $Group) {
if ($Group->getId() == $gid) {
if ($Group->getUUID() == $gid) {
return true;
}

Henning Leutz
committed
/**
* is the discount usable with this product?
*
* @param QUI\ERP\Products\Interfaces\ProductInterface $Product
* @return boolean
*/
public function canUsedWith(QUI\ERP\Products\Interfaces\ProductInterface $Product): bool

Henning Leutz
committed
{
if ($this->isActive() === false) {
return false;
}

Henning Leutz
committed
// coupon
if ($Product->getId() === -1) {

Henning Leutz
committed
return false;
}

Henning Leutz
committed
$articles = $this->getAttribute('articles');

Henning Leutz
committed
$categories = $this->getAttribute('categories');

Henning Leutz
committed
if (is_string($articles)) {
$articles = explode(',', $articles);

Henning Leutz
committed
}
if (is_string($categories)) {
$categories = explode(',', $categories);

Henning Leutz
committed
}

Henning Leutz
committed
// article / product check
if (empty($articles) && empty($categories)) {
return true;
}
// article / product check
foreach ($articles as $articleId) {
if ($Product->getId() === (int)$articleId) {

Henning Leutz
committed
}
}
// category check

Henning Leutz
committed
foreach ($categories as $category) {
$productCategories = $Product->getCategories();
foreach ($productCategories as $Category) {
/* @var $Category QUI\ERP\Products\Category\Category */
if ($Category->getId() === (int)$category) {

Henning Leutz
committed
return true;
}
}
}
return false;
}

Henning Leutz
committed
* @param QUI\ERP\ErpEntityInterface $Order

Henning Leutz
committed
public function canUsedInOrder(QUI\ERP\ErpEntityInterface $Order): bool
{
if ($this->isActive() === false) {
return false;
}

Henning Leutz
committed
if (!interface_exists('QUI\ERP\Order\OrderInterface')) {
return false;
}
if (!($Order instanceof QUI\ERP\Order\OrderInterface)) {
return false;
}
$Articles = $Order->getArticles();
foreach ($Articles as $Article) {
/* @var $Article QUI\ERP\Accounting\Article */
$id = $Article->getId();
continue;
}
try {
$Product = QUI\ERP\Products\Handler\Products::getProduct($id);
if ($this->canUsedWith($Product)) {
return true;
}
continue;
}
}
return false;
}
/**
* Verify the combination between the discounts
*
* @param Discount $Discount
* @throws QUI\ERP\Discount\Exception
public function verifyCombinationWith(Discount $Discount): void
{
if ($this->canCombinedWith($Discount) === false) {
'quiqqer/discount',
'exception.discount.not.combinable',
}
}
/**
* Verify the usage of the discount by the user
*
* @param User $User
* @throws QUI\ERP\Discount\Exception
public function verifyUser(User $User): void
'quiqqer/discount',
'exception.discount.user.cant.use.discount',
'id' => $this->getId(),
'userId' => $User->getUUID()
/**
* Parse the discount to a price factor
*
* @param null $Locale - optional, locale object
* @param null $Customer - optional,
* @return PriceFactorInterface|PriceFactorWithVatInterface|PriceFactor
public function toPriceFactor(
$Locale = null,
$Customer = null
): QUI\ERP\Products\Interfaces\PriceFactorInterface | QUI\ERP\Products\Interfaces\PriceFactorWithVatInterface | QUI\ERP\Products\Utils\PriceFactor {
switch ($this->getAttribute('discount_type')) {
case QUI\ERP\Accounting\Calc::CALCULATION_PERCENTAGE:
$calculation = QUI\ERP\Accounting\Calc::CALCULATION_PERCENTAGE;
case QUI\ERP\Accounting\Calc::CALCULATION_COMPLEMENT:
$calculation = QUI\ERP\Accounting\Calc::CALCULATION_COMPLEMENT;
$basis = match ($this->getAttribute('price_calculation_basis')) {
QUI\ERP\Accounting\Calc::CALCULATION_BASIS_NETTO => QUI\ERP\Accounting\Calc::CALCULATION_BASIS_NETTO,
default => QUI\ERP\Accounting\Calc::CALCULATION_BASIS_CURRENTPRICE,
};
// check calculation basis VAT
$useAuto = $this->getAttribute('consider_vat') === 'auto'
&& $Customer
&& QUI\ERP\Utils\User::isNettoUser($Customer) === false;
if ($useAuto || $this->getAttribute('consider_vat') === 'brutto') {
$basis = QUI\ERP\Accounting\Calc::CALCULATION_BASIS_VAT_BRUTTO;
}
if ($this->getAttribute('scope') === Handler::DISCOUNT_SCOPE_GRAND_TOTAL) {
$basis = QUI\ERP\Accounting\Calc::CALCULATION_GRAND_TOTAL;
}

Henning Leutz
committed
try {
$Plugin = QUI::getPackage('quiqqer/products');
$Config = $Plugin->getConfig();
$hideDiscounts = (int)$Config->getValue('products', 'hideDiscounts');

Henning Leutz
committed
$hideDiscounts = false;
}

Henning Leutz
committed
if ($this->getAttribute('scope') === Handler::DISCOUNT_SCOPE_TOTAL) {
if ($this->getAttribute('discount_type') === QUI\ERP\Accounting\Calc::CALCULATION_PERCENTAGE) {
$valueText = false;
} else {
$valueText = $this->getTitle($Locale);
}
'identifier' => 'discount-' . $this->getId(),
'title' => $this->getTitle($Locale),
'valueText' => $valueText,
'priority' => (int)$this->getAttribute('priority'),
'basis' => $basis,
'value' => $this->getAttribute('discount') * -1,
'visible' => !$hideDiscounts,
'vat' => $this->getAttribute('vat')
'identifier' => 'discount-' . $this->getId(),
'title' => $this->getTitle($Locale),
// 'valueText' => $this->getTitle($Locale),
'priority' => (int)$this->getAttribute('priority'),
'basis' => $basis,
'value' => $this->getAttribute('discount') * -1,

Patrick Müller
committed
/**
* Update the CRUD child
*
* @throws QUI\ExceptionStack|QUI\Exception
*/

Patrick Müller
committed
{
$this->Events->fireEvent('saveBegin');
$this->Events->fireEvent('updateBegin');
$needles = $this->Factory->getChildAttributes();

Patrick Müller
committed
$savedData = [];
foreach ($needles as $needle) {
if (!array_key_exists($needle, $this->attributes)) {

Patrick Müller
committed
continue;
}
$value = $this->getAttribute($needle);
if ($needle == 'user_groups') {
if (!empty($value)) {
$value = ',' . $value . ',';
}

Patrick Müller
committed
}
$savedData[$needle] = $value;
}
QUI::getDataBase()->update(
$this->Factory->getDataBaseTableName(),
$savedData,
['id' => $this->getId()]
);
$this->Events->fireEvent('saveEnd');
$this->Events->fireEvent('updateEnd');
}