From 04b2dd0d948b2ceaa9840b66bc16eb1895b5fac8 Mon Sep 17 00:00:00 2001 From: Henning Leutz <leutz@pcsg.de> Date: Tue, 19 Sep 2023 12:09:19 +0000 Subject: [PATCH] feat: shipping cost extended * Implemented new ERP Entity Interface * Extension of standard shipping costs: consider country of customer * Now when the address is changed AND the default shipping cost is assigned, the default shipping price will be adjusted accordingly --- composer.json | 68 ++++----- events.xml | 17 +++ locale.xml | 15 ++ settings.xml | 16 +++ src/QUI/ERP/Shipping/EventHandler.php | 109 ++++++++++++-- .../Shipping/Methods/Digital/ShippingType.php | 18 +-- .../Methods/Standard/ShippingType.php | 45 +++--- src/QUI/ERP/Shipping/Order/Shipping.php | 12 +- src/QUI/ERP/Shipping/Rules/ShippingRule.php | 40 +++--- src/QUI/ERP/Shipping/Shipping.php | 136 +++++++++--------- src/QUI/ERP/Shipping/Types/ShippingEntry.php | 85 ++++++----- 11 files changed, 352 insertions(+), 209 deletions(-) diff --git a/composer.json b/composer.json index da36db7..3ecc33e 100644 --- a/composer.json +++ b/composer.json @@ -1,38 +1,38 @@ { - "name": "quiqqer\/shipping", - "type": "quiqqer-plugin", - "description": "Provided the shipping management for QUIQQER. Can manage shipping methods", - "license": [ - "GPL-3.0+", - "PCSG QEL-1.0" - ], - "authors": [ - { - "name": "Henning Leutz", - "email": "leutz@pcsg.de", - "homepage": "http:\/\/www.pcsg.de", - "role": "Developer" - }, - { - "name": "Moritz Scholz", - "email": "scholz@pcsg.de", - "homepage": "http:\/\/www.pcsg.de", - "role": "Developer" - } - ], - "support": { - "email": "support@pcsg.de" + "name": "quiqqer\/shipping", + "type": "quiqqer-plugin", + "description": "Provided the shipping management for QUIQQER. Can manage shipping methods", + "license": [ + "GPL-3.0+", + "PCSG QEL-1.0" + ], + "authors": [ + { + "name": "Henning Leutz", + "email": "leutz@pcsg.de", + "homepage": "http:\/\/www.pcsg.de", + "role": "Developer" }, - "require": { - "quiqqer\/quiqqer": ">=1.5|dev-master|dev-dev", - "quiqqer\/erp": ">=1.0|dev-master|dev-dev", - "quiqqer\/areas": ">=1.1|dev-master|dev-dev", - "quiqqer\/order": ">=1.0|dev-master|dev-dev", - "quiqqer\/products": ">=1.5|dev-master|dev-dev" - }, - "autoload": { - "psr-4": { - "QUI\\ERP\\Shipping\\": "src\/QUI\/ERP\/Shipping" - } + { + "name": "Moritz Scholz", + "email": "scholz@pcsg.de", + "homepage": "http:\/\/www.pcsg.de", + "role": "Developer" + } + ], + "support": { + "email": "support@pcsg.de" + }, + "require": { + "quiqqer\/quiqqer": ">=1.5|dev-master|dev-dev", + "quiqqer\/erp": "^1.9|dev-master|dev-dev", + "quiqqer\/areas": "^1.1|dev-master|dev-dev", + "quiqqer\/order": "^1.0|dev-master|dev-dev", + "quiqqer\/products": "^1.5|dev-master|dev-dev" + }, + "autoload": { + "psr-4": { + "QUI\\ERP\\Shipping\\": "src\/QUI\/ERP\/Shipping" } + } } diff --git a/events.xml b/events.xml index 9c958c3..e807dc4 100644 --- a/events.xml +++ b/events.xml @@ -61,4 +61,21 @@ <event on="onQuiqqer::products::price::end" fire="\QUI\ERP\Shipping\EventHandler::onQuiqqerProductsPriceEnd" /> + + <!-- customer change --> + <event on="onQuiqqerOrderCustomerChange" + fire="\QUI\ERP\Shipping\EventHandler::onQuiqqerCustomerChange" + /> + <event on="onQuiqqerInvoiceCustomerChange" + fire="\QUI\ERP\Shipping\EventHandler::onQuiqqerCustomerChange" + /> + <event on="onQuiqqerOffersCustomerChange" + fire="\QUI\ERP\Shipping\EventHandler::onQuiqqerCustomerChange" + /> + <event on="onQuiqqerSalesOrderCustomerChange" + fire="\QUI\ERP\Shipping\EventHandler::onQuiqqerCustomerChange" + /> + <event on="onQuiqqerContractCustomerChange" + fire="\QUI\ERP\Shipping\EventHandler::onQuiqqerCustomerChange" + /> </events> diff --git a/locale.xml b/locale.xml index bb80e61..9e2fc83 100644 --- a/locale.xml +++ b/locale.xml @@ -306,6 +306,21 @@ <de><![CDATA[Lege fest welcher Preis als Standardversand verwendet werden soll.]]></de> <en><![CDATA[Specify which price should be used as default shipping.]]></en> </locale> + <locale name="shipping.defaultShipping.settings.considerCustomerCountry"> + <de><![CDATA[Land des Kunden beachten]]></de> + <en><![CDATA[Consider the country of the customer]]></en> + </locale> + <locale name="shipping.defaultShipping.settings.considerCustomerCountry.desc"> + <de><![CDATA[ + Wenn sich der Benutzer einer EntitÃĪt (zum Beispiel einer Rechnung oder Bestellung) ÃĪndert, + der zuvor der Standardversand zugewiesen war, wird versucht, den Versand basierend auf dem Land des Benutzers anzupassen. + ]]></de> + <en><![CDATA[ + When the user of an entity (for example, an invoice or an order) changes, + that was previously assigned the default shipping, an attempt is made to adjust the shipping based on the user's country. + ]]></en> + </locale> + <locale name="shipping.defaultShipping.settings.addDefaultShipping"> <de><![CDATA[Standard Versand automatisch hinzufÞgen]]></de> <en><![CDATA[Add standard shipping automatically]]></en> diff --git a/settings.xml b/settings.xml index b99f7a8..cac87d9 100644 --- a/settings.xml +++ b/settings.xml @@ -32,6 +32,10 @@ <conf name="defaultShippingPrice"> <type><![CDATA[string]]></type> </conf> + <conf name="considerCustomerCountry"> + <type><![CDATA[bool]]></type> + <defaultvalue>0</defaultvalue> + </conf> </section> <section name="no_rules"> @@ -179,6 +183,18 @@ </description> </input> + <input conf="shipping.considerCustomerCountry" type="checkbox"> + <text> + <locale group="quiqqer/shipping" + var="shipping.defaultShipping.settings.considerCustomerCountry"/> + </text> + <description> + <locale group="quiqqer/shipping" + var="shipping.defaultShipping.settings.considerCustomerCountry.desc"/> + </description> + </input> + + <input type="hidden" data-qui="package/quiqqer/translator/bin/controls/Update" data-qui-options-group="quiqqer/shipping" diff --git a/src/QUI/ERP/Shipping/EventHandler.php b/src/QUI/ERP/Shipping/EventHandler.php index 14652c0..ca5612e 100644 --- a/src/QUI/ERP/Shipping/EventHandler.php +++ b/src/QUI/ERP/Shipping/EventHandler.php @@ -8,17 +8,19 @@ use QUI; use QUI\ERP\Accounting\ArticleList; +use QUI\ERP\Order\AbstractOrder; use QUI\ERP\Order\Controls\OrderProcess\Checkout as OrderCheckoutStepControl; use QUI\ERP\Products\Handler\Fields as ProductFields; +use QUI\ERP\Shipping\Shipping as ShippingHandler; use Quiqqer\Engine\Collector; use function array_merge; use function explode; use function json_decode; use function method_exists; -use function str_replace; use function strpos; use function time; +use function usort; /** * Class EventHandler @@ -122,12 +124,12 @@ public static function onAdminLoadFooter() * event - on price factor init * * @param $Basket - * @param QUI\ERP\Order\AbstractOrder $Order + * @param AbstractOrder $Order * @param QUI\ERP\Products\Product\ProductList $Products */ public static function onQuiqqerOrderBasketToOrderEnd( $Basket, - QUI\ERP\Order\AbstractOrder $Order, + AbstractOrder $Order, QUI\ERP\Products\Product\ProductList $Products ) { if (Shipping::getInstance()->shippingDisabled()) { @@ -520,10 +522,10 @@ public static function onQuiqqerProductsPriceEnd(Collector $Collector, QUI\ERP\P /** * event: add default shipping at onQuiqqerOrderFactoryCreate * - * @param \QUI\ERP\Order\AbstractOrder $Order + * @param AbstractOrder $Order * @return void */ - public static function onQuiqqerOrderFactoryCreate(QUI\ERP\Order\AbstractOrder $Order) + public static function onQuiqqerOrderFactoryCreate(AbstractOrder $Order) { //if ($Order->getCustomDataEntry(self::DEFAULT_SHIPPING_TIME_KEY)) { // return; @@ -649,11 +651,11 @@ protected static function addDefaultShipping(ArticleList $Articles) //endregion /** - * @param \QUI\ERP\Order\AbstractOrder $Order + * @param AbstractOrder $Order * @return void */ public static function onQuiqqerOrderUpdateBegin( - QUI\ERP\Order\AbstractOrder $Order, + AbstractOrder $Order, &$data = [] ) { $Articles = $Order->getArticles(); @@ -668,10 +670,6 @@ public static function onQuiqqerOrderUpdateBegin( $factors = $PriceFactors->toArray(); $Shipping = $Order->getShipping(); - if (!$Shipping) { - return; - } - foreach ($factors as $index => $factor) { if (strpos($factor['identifier'], 'shipping-pricefactor-') !== false) { $shippingFactor = $factor; @@ -679,15 +677,29 @@ public static function onQuiqqerOrderUpdateBegin( } } - if (!$shippingFactor) { + if (!$shippingFactor && !$Shipping) { return; } $identifier = $shippingFactor['identifier']; - $identifier = str_replace($identifier, 'shipping-pricefactor-', ''); - $id = (int)$identifier; + $identifier = str_replace('shipping-pricefactor-', '', $identifier); + $id = $identifier; + $id = (int)$id; + + if ($Order->getAttribute('__SHIPPING__')) { + $Shipping = $Order->getAttribute('__SHIPPING__'); + $Order->setShipping($Shipping); - if ($id !== $Shipping->getId() && isset($index)) { + if ($Shipping->getId() === $id) { + return; + } + } + + if (!$Shipping && isset($index) && $identifier !== 'default') { + // kill shipping factor + $PriceFactors->removeFactor($index); + } elseif ($Shipping && $id !== $Shipping->getId() && isset($index)) { + // replace shipping $Factor = $PriceFactors->getFactor($index); $factor = $Factor->toArray(); @@ -700,6 +712,73 @@ public static function onQuiqqerOrderUpdateBegin( ); $data['articles'] = $Articles->toJSON(); + } elseif ($Shipping && !isset($index)) { + $PriceFactors->addFactor($Shipping->toPriceFactor()->toErpPriceFactor()); + } elseif (!$Shipping && isset($index) && $identifier === 'default') { + self::onQuiqqerCustomerChange($Order); + } + } + + public static function onQuiqqerCustomerChange(QUI\ERP\ErpEntityInterface $ErpEntity) + { + try { + if (!QUI::getPackage('quiqqer/shipping')->getConfig()->get('shipping', 'considerCustomerCountry')) { + return; + } + } catch (\Exception $exception) { + return; + } + + $Articles = $ErpEntity->getArticles(); + $PriceFactors = $Articles->getPriceFactors(); + $shippingEntries = ShippingHandler::getInstance()->getValidShippingEntries($ErpEntity); + + if (empty($shippingEntries)) { + return; + } + + // sort by price + usort($shippingEntries, function ($ShippingEntryA, $ShippingEntryB) { + $priorityA = $ShippingEntryA->getAttribute('priority'); + $priorityB = $ShippingEntryB->getAttribute('priority'); + + if ($priorityA === $priorityB) { + return 0; + } + + return $priorityA < $priorityB ? -1 : 1; + }); + + + /* @var $PriceFactor QUI\ERP\Accounting\PriceFactors\Factor */ + foreach ($PriceFactors as $index => $PriceFactor) { + if (strpos($PriceFactor->getIdentifier(), 'shipping-pricefactor-') === false) { + continue; + } + + $ShippingEntry = null; + + // set the shipping for the order + if ($ErpEntity instanceof AbstractOrder) { + foreach ($shippingEntries as $Entry) { + try { + $ErpEntity->setShipping($Entry); + $ErpEntity->setAttribute('__SHIPPING__', $Entry); + $ShippingEntry = $Entry; + break; + } catch (QUI\Exception $exception) { + } + } + } else { + $ShippingEntry = $shippingEntries[0]; + } + + if ($ShippingEntry) { + $PriceFactor = $ShippingEntry->toPriceFactor(QUI::getLocale(), $ErpEntity); + $PriceFactors->setFactor($index, $PriceFactor->toErpPriceFactor()); + } + + return; } } } diff --git a/src/QUI/ERP/Shipping/Methods/Digital/ShippingType.php b/src/QUI/ERP/Shipping/Methods/Digital/ShippingType.php index dbdde94..1aaa082 100644 --- a/src/QUI/ERP/Shipping/Methods/Digital/ShippingType.php +++ b/src/QUI/ERP/Shipping/Methods/Digital/ShippingType.php @@ -39,12 +39,12 @@ public function getIcon() } /** - * @param QUI\ERP\Order\OrderInterface $Order + * @param QUI\ERP\ErpEntityInterface $Entity * @param QUI\ERP\Shipping\Types\ShippingEntry $ShippingEntry * @return bool */ public function canUsedInOrder( - QUI\ERP\Order\OrderInterface $Order, + QUI\ERP\ErpEntityInterface $Entity, QUI\ERP\Shipping\Types\ShippingEntry $ShippingEntry ) { if ($ShippingEntry->isActive() === false) { @@ -59,7 +59,7 @@ public function canUsedInOrder( // Check if order contains NON-digital products /** @var QUI\ERP\Accounting\Article $Article */ - foreach ($Order->getArticles() as $Article) { + foreach ($Entity->getArticles() as $Article) { try { // Do not parse coupon codes / discounts if (empty($Article->getId()) || !\is_numeric($Article->getId())) { @@ -109,10 +109,10 @@ public function canUsedInOrder( return true; } - $ArticleList = $Order->getArticles(); - $orderArticles = $ArticleList->getArticles(); + $ArticleList = $Entity->getArticles(); + $entityArticles = $ArticleList->getArticles(); - foreach ($orderArticles as $Article) { + foreach ($entityArticles as $Article) { try { $productId = $Article->getId(); @@ -154,13 +154,13 @@ public function canUsedInOrder( /** * @param QUI\Interfaces\Users\User $User * @param QUI\ERP\Shipping\Api\ShippingInterface $ShippingEntry - * @param QUI\ERP\Order\AbstractOrder $Order + * @param QUI\ERP\ErpEntityInterface $Entity * @return bool */ public function canUsedBy( QUI\Interfaces\Users\User $User, QUI\ERP\Shipping\Api\ShippingInterface $ShippingEntry, - QUI\ERP\Order\AbstractOrder $Order + QUI\ERP\ErpEntityInterface $Entity ) { if ($ShippingEntry->isActive() === false) { Debug::addLog("{$this->getTitle()} :: {$ShippingEntry->getTitle()} :: is not active"); @@ -186,7 +186,7 @@ public function canUsedBy( return true; } - $Address = $Order->getDeliveryAddress(); + $Address = $Entity->getDeliveryAddress(); $areasValue = \trim($areasValue); $areasValue = \trim($areasValue, ','); diff --git a/src/QUI/ERP/Shipping/Methods/Standard/ShippingType.php b/src/QUI/ERP/Shipping/Methods/Standard/ShippingType.php index 3096d54..214266c 100644 --- a/src/QUI/ERP/Shipping/Methods/Standard/ShippingType.php +++ b/src/QUI/ERP/Shipping/Methods/Standard/ShippingType.php @@ -16,6 +16,8 @@ use function array_map; use function explode; use function in_array; +use function is_numeric; +use function method_exists; use function trim; /** @@ -50,12 +52,12 @@ public function getIcon() } /** - * @param QUI\ERP\Order\OrderInterface $Order + * @param QUI\ERP\ErpEntityInterface $Entity * @param QUI\ERP\Shipping\Types\ShippingEntry $ShippingEntry * @return bool */ public function canUsedInOrder( - QUI\ERP\Order\OrderInterface $Order, + QUI\ERP\ErpEntityInterface $Entity, QUI\ERP\Shipping\Types\ShippingEntry $ShippingEntry ) { if ($ShippingEntry->isActive() === false) { @@ -68,14 +70,20 @@ public function canUsedInOrder( return false; } + try { + $ArticleList = $Entity->getArticles(); + } catch (\Exception $exception) { + return false; + } + // Check if order contains only digital products $digitalProductsOnly = true; /** @var QUI\ERP\Accounting\Article $Article */ - foreach ($Order->getArticles() as $Article) { + foreach ($ArticleList as $Article) { try { // Do not parse coupon codes / discounts - if (empty($Article->getId()) || !\is_numeric($Article->getId())) { + if (empty($Article->getId()) || !is_numeric($Article->getId())) { continue; } @@ -127,10 +135,9 @@ public function canUsedInOrder( return true; } - $ArticleList = $Order->getArticles(); - $orderArticles = $ArticleList->getArticles(); + $entityArticles = $ArticleList->getArticles(); - foreach ($orderArticles as $Article) { + foreach ($entityArticles as $Article) { try { $productId = $Article->getId(); @@ -172,13 +179,13 @@ public function canUsedInOrder( /** * @param QUI\Interfaces\Users\User $User * @param QUI\ERP\Shipping\Api\ShippingInterface $ShippingEntry - * @param QUI\ERP\Order\AbstractOrder $Order + * @param QUI\ERP\ErpEntityInterface $Entity * @return bool */ public function canUsedBy( QUI\Interfaces\Users\User $User, QUI\ERP\Shipping\Api\ShippingInterface $ShippingEntry, - QUI\ERP\Order\AbstractOrder $Order + QUI\ERP\ErpEntityInterface $Entity ) { if ($ShippingEntry->isActive() === false) { Debug::addLog("{$this->getTitle()} :: {$ShippingEntry->getTitle()} :: is not active"); @@ -204,18 +211,20 @@ public function canUsedBy( return true; } - $Address = $Order->getDeliveryAddress(); + if (method_exists($Entity, 'getDeliveryAddress')) { + $Address = $Entity->getDeliveryAddress(); - $areasValue = trim($areasValue); - $areasValue = trim($areasValue, ','); - $areasValue = explode(',', $areasValue); - $areasValue = array_filter($areasValue); + $areasValue = trim($areasValue); + $areasValue = trim($areasValue, ','); + $areasValue = explode(',', $areasValue); + $areasValue = array_filter($areasValue); - // not in area - if (!empty($areasValue) && !AreaUtils::isAddressInArea($Address, $areasValue)) { - Debug::addLog("{$this->getTitle()} :: {$ShippingEntry->getTitle()} :: User is not in areas"); + // not in area + if (!empty($areasValue) && !AreaUtils::isAddressInArea($Address, $areasValue)) { + Debug::addLog("{$this->getTitle()} :: {$ShippingEntry->getTitle()} :: User is not in areas"); - return false; + return false; + } } Debug::addLog("{$this->getTitle()} :: {$ShippingEntry->getTitle()} :: User is in areas"); diff --git a/src/QUI/ERP/Shipping/Order/Shipping.php b/src/QUI/ERP/Shipping/Order/Shipping.php index 4c8a952..b432a01 100644 --- a/src/QUI/ERP/Shipping/Order/Shipping.php +++ b/src/QUI/ERP/Shipping/Order/Shipping.php @@ -75,14 +75,14 @@ public function getBody() $Logger = QUI\ERP\Shipping\Debug::getLoggerWithoutFormatter(); foreach ($debugShipping as $DebugShippingEntry) { - $DebugShippingEntry->setOrder($Order); + $DebugShippingEntry->setErpEntity($Order); QUI\ERP\Shipping\Debug::enable(); QUI\ERP\Shipping\Debug::addLog('# ' . $DebugShippingEntry->getTitle()); if ($DebugShippingEntry->canUsedBy($User, $Order)) { $DebugShippingEntry->isValid(); - $DebugShippingEntry->canUsedInOrder($Order); + $DebugShippingEntry->canUsedInErpEntity($Order); $DebugShippingEntry->canUsedBy($User, $Order); } @@ -205,7 +205,7 @@ public function validate() ]); } - if (!$Shipping->canUsedInOrder($Order)) { + if (!$Shipping->canUsedInErpEntity($Order)) { throw new QUI\ERP\Order\Exception([ 'quiqqer/shipping', 'exception.shipping.is.not.allowed' @@ -218,7 +218,7 @@ public function validate() */ protected function getValidShipping() { - return ShippingHandler::getInstance()->getValidShippingEntriesByOrder($this->getOrder()); + return ShippingHandler::getInstance()->getValidShippingEntries($this->getOrder()); } /** @@ -245,13 +245,13 @@ public function save() try { $Shipping = QUI\ERP\Shipping\Shipping::getInstance(); $ShippingEntry = $Shipping->getShippingEntry($shipping); - $ShippingEntry->setOrder($Order); + $ShippingEntry->setErpEntity($Order); if (!$ShippingEntry->canUsedBy($User, $Order)) { return; } - if (!$ShippingEntry->canUsedInOrder($Order)) { + if (!$ShippingEntry->canUsedInErpEntity($Order)) { return; } } catch (QUI\ERP\Shipping\Exception $Exception) { diff --git a/src/QUI/ERP/Shipping/Rules/ShippingRule.php b/src/QUI/ERP/Shipping/Rules/ShippingRule.php index 2ee62e0..15afc7b 100644 --- a/src/QUI/ERP/Shipping/Rules/ShippingRule.php +++ b/src/QUI/ERP/Shipping/Rules/ShippingRule.php @@ -287,7 +287,7 @@ public function canUsedBy(QUI\Interfaces\Users\User $User) } // not in area - // address need to be checked via order + // address need to be checked via erp entity // user checking $userGroups = QUI\Utils\UserGroups::parseUsersGroupsString( @@ -355,12 +355,12 @@ public function canUsedWithAddress($Address) } /** - * is the shipping allowed in the order? + * is the shipping allowed in this erp entity? * - * @param QUI\ERP\Order\OrderInterface|null $Order + * @param QUI\ERP\ErpEntityInterface|null $ErpEntity * @return bool */ - public function canUsedInOrder($Order) + public function canUsedIn(QUI\ERP\ErpEntityInterface $ErpEntity = null): bool { if (!$this->isValid()) { Debug::addLog("{$this->getTitle()} :: is not valid"); @@ -368,17 +368,17 @@ public function canUsedInOrder($Order) return false; } - if (!($Order instanceof QUI\ERP\Order\OrderInterface)) { + if (!$ErpEntity) { return true; } - if (!$this->canUsedBy($Order->getCustomer())) { - Debug::addLog("{$this->getTitle()} :: can not be used by {$Order->getCustomer()->getId()}"); + if (!$this->canUsedBy($ErpEntity->getCustomer())) { + Debug::addLog("{$this->getTitle()} :: can not be used by {$ErpEntity->getCustomer()->getId()}"); return false; } - $DeliveryAddress = $Order->getDeliveryAddress(); + $DeliveryAddress = $ErpEntity->getDeliveryAddress(); if (!$this->canUsedWithAddress($DeliveryAddress)) { Debug::addLog("{$this->getTitle()} :: can not be used with address"); @@ -386,8 +386,7 @@ public function canUsedInOrder($Order) return false; } - /* @var $Order QUI\ERP\Order\Order */ - $Articles = $Order->getArticles(); + $Articles = $ErpEntity->getArticles(); $articleList = $Articles->getArticles(); if (!$Articles->count()) { @@ -432,7 +431,7 @@ public function canUsedInOrder($Order) $articleOnly = 0; } - $categoryIdsInOrder = []; + $categoryIdsInEntity = []; $checkCategoryIds = function (array $catIds) use ($categories) { foreach ($catIds as $categoryId) { @@ -462,7 +461,7 @@ public function canUsedInOrder($Order) return false; } - $categoryIdsInOrder = array_merge($categoryIdsInOrder, $categoryIds); + $categoryIdsInEntity = array_merge($categoryIdsInEntity, $categoryIds); foreach ($unitIds as $unitId) { @@ -527,17 +526,17 @@ public function canUsedInOrder($Order) } // category check - $categoryIdsInOrder = array_unique($categoryIdsInOrder); + $categoryIdsInEntity = array_unique($categoryIdsInEntity); if ( - !empty($categoryIdsInOrder) + !empty($categoryIdsInEntity) && !empty($categories) && (count($categories) === 1 && $categories[0] !== 0) ) { $found = false; foreach ($categories as $cid) { - if (in_array($cid, $categoryIdsInOrder)) { + if (in_array($cid, $categoryIdsInEntity)) { $found = true; break; } @@ -649,7 +648,7 @@ public function canUsedInOrder($Order) // quantity check - $count = $Order->count(); + $count = $ErpEntity->count(); if (!empty($quantityFrom) && $quantityFrom < $count) { QUI\ERP\Shipping\Debug::addLog( @@ -669,9 +668,9 @@ public function canUsedInOrder($Order) // purchase try { - $Calculation = $Order->getPriceCalculation(); - $Currency = $Order->getCurrency(); - $PriceFactors = $Order->getArticles()->getPriceFactors(); + $Calculation = $ErpEntity->getPriceCalculation(); + $Currency = $ErpEntity->getCurrency(); + $PriceFactors = $ErpEntity->getArticles()->getPriceFactors(); $ShippingFactor = null; /* @var $Factor QUI\ERP\Products\Interfaces\PriceFactorInterface */ @@ -723,7 +722,8 @@ public function canUsedInOrder($Order) try { - QUI::getEvents()->fireEvent('shippingCanUsedInOrder', [$this, $Order]); + QUI::getEvents()->fireEvent('shippingCanUsedInOrder', [$this, $ErpEntity]); + QUI::getEvents()->fireEvent('shippingCanUsedInEntity', [$this, $ErpEntity]); } catch (ShippingCanNotBeUsed $Exception) { return false; } catch (QUI\Exception $Exception) { diff --git a/src/QUI/ERP/Shipping/Shipping.php b/src/QUI/ERP/Shipping/Shipping.php index 4734c30..e3ac2fa 100644 --- a/src/QUI/ERP/Shipping/Shipping.php +++ b/src/QUI/ERP/Shipping/Shipping.php @@ -8,15 +8,22 @@ use QUI; use QUI\ERP\Order\AbstractOrder; +use QUI\ERP\Products\Utils\PriceFactor; use QUI\ERP\Shipping\Api\AbstractShippingProvider; use QUI\ERP\Shipping\Types\Factory; +use QUI\Interfaces\Users\User; +use function array_filter; use function array_keys; +use function array_map; use function class_exists; use function count; +use function explode; use function key; use function max; +use function method_exists; use function strpos; +use function trim; /** * Shipping @@ -48,31 +55,31 @@ class Shipping extends QUI\Utils\Singleton /** * @var array */ - protected $shipping = []; + protected array $shipping = []; /** * @var bool */ - protected $debugging = null; + protected ?bool $debugging = null; /** * @var null */ - protected $shippingDisabled = null; + protected ?bool $shippingDisabled = null; /** * Return all available shipping provider * * @return array */ - public function getShippingProviders() + public function getShippingProviders(): array { $cacheProvider = 'package/quiqqer/shipping/provider'; try { $providers = QUI\Cache\Manager::get($cacheProvider); } catch (QUI\Cache\Exception $Exception) { - $packages = \array_map(function ($package) { + $packages = array_map(function ($package) { return $package['name']; }, QUI::getPackageManager()->getInstalled()); @@ -121,7 +128,7 @@ public function getShippingProviders() * * @return bool */ - public function shippingDisabled() + public function shippingDisabled(): bool { if ($this->shippingDisabled !== null) { return $this->shippingDisabled; @@ -142,7 +149,7 @@ public function shippingDisabled() * * @return bool */ - public function debuggingEnabled() + public function debuggingEnabled(): bool { if ($this->debugging !== null) { return $this->debugging; @@ -163,7 +170,7 @@ public function debuggingEnabled() * * @return array */ - public function getShippingTypes() + public function getShippingTypes(): array { $shipping = []; $providers = $this->getShippingProviders(); @@ -194,7 +201,7 @@ public function getShippingTypes() * @return QUI\ERP\Shipping\Api\ShippingTypeInterface * @throws Exception */ - public function getShippingType($shippingType) + public function getShippingType(string $shippingType): Api\ShippingTypeInterface { if (empty($shippingType)) { throw new Exception([ @@ -228,7 +235,7 @@ public function getShippingType($shippingType) * * @throws Exception */ - public function getShippingEntry($shippingId) + public function getShippingEntry($shippingId): Types\ShippingEntry { try { return Factory::getInstance()->getChild($shippingId); @@ -246,7 +253,7 @@ public function getShippingEntry($shippingId) * @param array $queryParams * @return QUI\ERP\Shipping\Types\ShippingEntry[] */ - public function getShippingList($queryParams = []) + public function getShippingList(array $queryParams = []): array { if (!isset($queryParams['order'])) { $queryParams['order'] = 'priority ASC'; @@ -262,42 +269,41 @@ public function getShippingList($queryParams = []) /** * Return all shipping entries for the user * - * @param \QUI\Interfaces\Users\User|null $User - optional - * @param QUI\ERP\Order\AbstractOrder $Order - optional + * @param User|null $User - optional + * @param QUI\ERP\ErpEntityInterface|null $Entity - optional * @return QUI\ERP\Shipping\Types\ShippingEntry[] */ - public function getUserShipping($User = null, $Order = null) + public function getUserShipping(User $User = null, QUI\ERP\ErpEntityInterface $Entity = null): array { if ($User === null) { $User = QUI::getUserBySession(); } - if ($Order === null) { + if ($Entity === null) { return []; } - return \array_filter($this->getShippingList(), function ($Shipping) use ($User, $Order) { - /* @var $Shipping QUI\ERP\Shipping\Types\ShippingEntry */ + return array_filter($this->getShippingList(), function ($Shipping) use ($User, $Entity) { if ($Shipping->isActive() === false) { return false; } - return $Shipping->canUsedBy($User, $Order); + return $Shipping->canUsedBy($User, $Entity); }); } /** - * Return the shipping price factor of an order + * Return the shipping price factor of an erp entity * - * @param AbstractOrder $Order + * @param QUI\ERP\ErpEntityInterface $Entity * @return QUI\ERP\Products\Interfaces\PriceFactorInterface */ - public function getShippingPriceFactorByOrder(AbstractOrder $Order) - { - $PriceFactors = $Order->getArticles()->getPriceFactors(); + public function getShippingPriceFactor( + QUI\ERP\ErpEntityInterface $Entity + ): ?QUI\ERP\Products\Interfaces\PriceFactorInterface { + $PriceFactors = $Entity->getArticles()->getPriceFactors(); foreach ($PriceFactors as $PriceFactor) { - /* @var $PriceFactor QUI\ERP\Products\Utils\PriceFactor */ if (strpos($PriceFactor->getIdentifier(), 'shipping-pricefactor') !== false) { return $PriceFactor; } @@ -307,25 +313,27 @@ public function getShippingPriceFactorByOrder(AbstractOrder $Order) } /** - * Get all valid shipping entries for an order + * Get all valid shipping entries for an erp entity * - * @param QUI\ERP\Order\AbstractOrder $Order + * @param QUI\ERP\ErpEntityInterface $Entity * @return QUI\ERP\Shipping\Types\ShippingEntry[] */ - public function getValidShippingEntriesByOrder(AbstractOrder $Order) + public function getValidShippingEntries(QUI\ERP\ErpEntityInterface $Entity): array { - $User = $Order->getCustomer(); + $User = $Entity->getCustomer(); - $userShipping = QUI\ERP\Shipping\Shipping::getInstance()->getUserShipping($User, $Order); + $userShipping = QUI\ERP\Shipping\Shipping::getInstance()->getUserShipping($User, $Entity); $shippingList = []; foreach ($userShipping as $ShippingEntry) { - $ShippingEntry->setOrder($Order); + if ($Entity instanceof QUI\ERP\ErpEntityInterface) { + $ShippingEntry->setErpEntity($Entity); + } if ( $ShippingEntry->isValid() - && $ShippingEntry->canUsedInOrder($Order) - && $ShippingEntry->canUsedBy($User, $Order) + && $ShippingEntry->canUsedInErpEntity($Entity) + && $ShippingEntry->canUsedBy($User, $Entity) ) { $shippingList[] = $ShippingEntry; } @@ -339,7 +347,7 @@ public function getValidShippingEntriesByOrder(AbstractOrder $Order) * * @return array */ - public function getShippingRuleUnitFieldIds() + public function getShippingRuleUnitFieldIds(): array { try { $Config = QUI::getPackage('quiqqer/shipping')->getConfig(); @@ -353,13 +361,13 @@ public function getShippingRuleUnitFieldIds() return [QUI\ERP\Products\Handler\Fields::FIELD_WEIGHT]; } - return \explode(',', $ids); + return explode(',', $ids); } /** - * @return bool|string + * @return string */ - public function getHost() + public function getHost(): string { try { $Project = QUI::getRewrite()->getProject(); @@ -373,34 +381,25 @@ public function getHost() } } - $host = $Project->getVHost(true, true); - $host = \trim($host, '/'); - - return $host; + return trim( + $Project->getVHost(true, true), + '/' + ); } /** - * @param QUI\ERP\Order\Order| - * QUI\ERP\Order\OrderInProcess| - * QUI\ERP\Accounting\Invoice\Invoice| - * QUI\ERP\Accounting\Invoice\InvoiceTemporary - * $Order + * @param QUI\ERP\ErpEntityInterface $Entity * * @return QUI\ERP\Shipping\Types\ShippingEntry|QUI\ERP\Shipping\Types\ShippingUnique */ - public function getShippingByObject($Object) + public function getShippingByObject(QUI\ERP\ErpEntityInterface $Entity) { - if ( - !($Object instanceof QUI\ERP\Order\Order) && - !($Object instanceof QUI\ERP\Order\OrderInProcess) && - !($Object instanceof QUI\ERP\Accounting\Invoice\Invoice) && - !($Object instanceof QUI\ERP\Accounting\Invoice\InvoiceTemporary) - ) { - return null; - } + $Shipping = null; + $Delivery = $Entity->getDeliveryAddress(); - $Shipping = $Object->getShipping(); - $Delivery = $Object->getDeliveryAddress(); + if (method_exists($Entity, 'getShipping')) { + $Shipping = $Entity->getShipping(); + } if ($Delivery && $Shipping) { $Shipping->setAddress($Delivery); @@ -425,10 +424,10 @@ public function getShippingByOrderId($orderId) } /** - * @return \QUI\ERP\Products\Utils\PriceFactor + * @return PriceFactor * @throws \QUI\Exception */ - public function getDefaultPriceFactor(): QUI\ERP\Products\Utils\PriceFactor + public function getDefaultPriceFactor(): PriceFactor { $price = QUI::getPackage('quiqqer/shipping') ->getConfig() @@ -436,7 +435,7 @@ public function getDefaultPriceFactor(): QUI\ERP\Products\Utils\PriceFactor $price = QUI\ERP\Money\Price::validatePrice($price); - $PriceFactor = new QUI\ERP\Products\Utils\PriceFactor([ + $PriceFactor = new PriceFactor([ 'identifier' => 'shipping-pricefactor-default', 'title' => QUI::getLocale()->get('quiqqer/shipping', 'shipping.default.pricefactor'), 'description' => '', @@ -461,15 +460,15 @@ public function getDefaultPriceFactor(): QUI\ERP\Products\Utils\PriceFactor /** * Returns the var of an order * - * @param $Order + * @param QUI\ERP\ErpEntityInterface $ErpEntity * @return float|int|mixed|string|null * @throws \QUI\Exception */ - public function getOrderVat($Order) + public function getVat(QUI\ERP\ErpEntityInterface $ErpEntity) { /* @var $Article QUI\ERP\Accounting\Article */ - $Articles = $Order->getArticles(); + $Articles = $ErpEntity->getArticles(); $vats = []; foreach ($Articles as $Article) { @@ -485,7 +484,7 @@ public function getOrderVat($Order) // @todo implement VAT setting for shipping // look at vat, which vat should be used - if (!count($vats) && !$Order->getCustomer()) { + if (!count($vats) && !$ErpEntity->getCustomer()) { // use default vat $Area = QUI\ERP\Defaults::getArea(); $TaxType = QUI\ERP\Tax\Utils::getTaxTypeByArea($Area); @@ -494,8 +493,8 @@ public function getOrderVat($Order) return $TaxEntry->getValue(); } - if (!count($vats) && $Order->getCustomer()) { - $Tax = QUI\ERP\Tax\Utils::getTaxByUser($Order->getCustomer()); + if (!count($vats) && $ErpEntity->getCustomer()) { + $Tax = QUI\ERP\Tax\Utils::getTaxByUser($ErpEntity->getCustomer()); return $Tax->getValue(); } @@ -517,15 +516,15 @@ public function getOrderVat($Order) * * @param QUI\ERP\Order\AbstractOrder $Order * @param int $statusId - * @param string $message (optional) - Custom notification message [default: default status change message] + * @param string|null $message (optional) - Custom notification message [default: default status change message] * @return void * * @throws QUI\Exception */ public function sendStatusChangeNotification( AbstractOrder $Order, - $statusId, - $message = null + int $statusId, + string $message = null ) { $Customer = $Order->getCustomer(); $customerEmail = $Customer->getAttribute('email'); @@ -545,7 +544,6 @@ public function sendStatusChangeNotification( } $Mailer = new QUI\Mail\Mailer(); - /** @var QUI\Locale $Locale */ $Locale = $Order->getCustomer()->getLocale(); $Mailer->setSubject( diff --git a/src/QUI/ERP/Shipping/Types/ShippingEntry.php b/src/QUI/ERP/Shipping/Types/ShippingEntry.php index 94171b0..cbd7e8a 100644 --- a/src/QUI/ERP/Shipping/Types/ShippingEntry.php +++ b/src/QUI/ERP/Shipping/Types/ShippingEntry.php @@ -34,9 +34,9 @@ class ShippingEntry extends QUI\CRUD\Child implements Api\ShippingInterface { /** - * @var null + * @var QUI\ERP\ErpEntityInterface|null */ - protected $Order = null; + protected ?QUI\ERP\ErpEntityInterface $ErpEntity = null; /** * @var null|QUI\ERP\Address|QUI\Users\Address @@ -192,12 +192,11 @@ public function getPriceDisplay(): string { $PriceFactor = $this->toPriceFactor(); - $Order = $this->Order; + $ErpEntity = $this->ErpEntity; $isNetto = false; - /* @var $Order QUI\ERP\Order\Order */ - if ($Order) { - $Customer = $Order->getCustomer(); + if ($ErpEntity) { + $Customer = $ErpEntity->getCustomer(); $isNetto = $Customer->isNetto(); } @@ -238,7 +237,7 @@ public function getPrice() $rules = $this->getShippingRules(); $price = 0; - $Order = $this->Order; + $ErpEntity = $this->ErpEntity; foreach ($rules as $Rule) { $discount = $Rule->getAttribute('discount'); @@ -249,11 +248,10 @@ public function getPrice() continue; } - if ($type === QUI\ERP\Shipping\Rules\Factory::DISCOUNT_TYPE_PC_ORDER && $Order) { + if ($type === QUI\ERP\Shipping\Rules\Factory::DISCOUNT_TYPE_PC_ORDER && $ErpEntity) { try { - /* @var $Order QUI\ERP\Order\Order */ - $Order = $this->Order; - $Calculation = $Order->getPriceCalculation(); + $ErpEntity = $this->ErpEntity; + $Calculation = $ErpEntity->getPriceCalculation(); $nettoSum = $Calculation->getNettoSum()->get(); if (!$nettoSum) { @@ -284,13 +282,13 @@ public function getPrice() * is the user allowed to use this shipping * * @param QUI\Interfaces\Users\User $User - * @param QUI\ERP\Order\AbstractOrder $Order + * @param QUI\ERP\ErpEntityInterface $Entity * * @return boolean */ public function canUsedBy( QUI\Interfaces\Users\User $User, - QUI\ERP\Order\AbstractOrder $Order + QUI\ERP\ErpEntityInterface $Entity ): bool { if ($this->isActive() === false) { return false; @@ -300,7 +298,7 @@ public function canUsedBy( $ShippingType = $this->getShippingType(); if (method_exists($ShippingType, 'canUsedBy')) { - return $ShippingType->canUsedBy($User, $this, $Order); + return $ShippingType->canUsedBy($User, $this, $Entity); } } catch (Exception $Exception) { return false; @@ -310,13 +308,13 @@ public function canUsedBy( } /** - * is the shipping allowed in the order? + * is the shipping allowed in this erp entity? * - * @param QUI\ERP\Order\OrderInterface $Order + * @param QUI\ERP\ErpEntityInterface $Entity * * @return bool */ - public function canUsedInOrder(QUI\ERP\Order\OrderInterface $Order): bool + public function canUsedInErpEntity(QUI\ERP\ErpEntityInterface $Entity): bool { if ($this->isActive() === false) { Debug::addLog($this->getTitle() . ' is not active'); @@ -327,8 +325,8 @@ public function canUsedInOrder(QUI\ERP\Order\OrderInterface $Order): bool try { $ShippingType = $this->getShippingType(); - if (method_exists($ShippingType, 'canUsedInOrder')) { - return $ShippingType->canUsedInOrder($Order, $this); + if (method_exists($ShippingType, 'canUsedIn')) { + return $ShippingType->canUsedIn($Entity, $this); } } catch (Exception $Exception) { return false; @@ -660,9 +658,9 @@ public function getShippingRules(): array Debug::addLog("### {$Rule->getTitle()} is valid"); } - if (!$Rule->canUsedInOrder($this->Order)) { + if (!$Rule->canUsedIn($this->ErpEntity)) { if ($debugging) { - Debug::addLog("### {$Rule->getTitle()} can not used in order"); + Debug::addLog("### {$Rule->getTitle()} can not used in this entity"); $debuggingLog[] = [ 'id' => $Rule->getId(), @@ -676,7 +674,7 @@ public function getShippingRules(): array } if ($debugging) { - Debug::addLog("### {$Rule->getTitle()} can used in order"); + Debug::addLog("### {$Rule->getTitle()} can used in this entity"); } $result[] = $Rule; @@ -734,6 +732,8 @@ public function isValid(): bool $rules = $this->getShippingRules(); + // @todo ist das so gewollt? + // wenn keine rules zugewiesen sind, das der shipping entry nie nutzbar ist? if (empty($rules)) { Debug::addLog("{$this->getTitle()} :: has no active rules"); @@ -747,41 +747,50 @@ public function isValid(): bool //endregion + /** - * Set an order to the shipping entry - * this order is then assigned to the shipping and the validation considers this order + * Set an erp entity to the shipping entry + * this erp entity is then assigned to the shipping and the validation considers this erp entity * - * @param QUI\ERP\Order\OrderInterface $Order + * @param QUI\ERP\ErpEntityInterface $ErpEntity + */ + public function setErpEntity(QUI\ERP\ErpEntityInterface $ErpEntity) + { + $this->ErpEntity = $ErpEntity; + } + + /** + * @deprecated use setErpEntity() */ - public function setOrder(QUI\ERP\Order\OrderInterface $Order) + public function setOrder(QUI\ERP\ErpEntityInterface $ErpEntity) { - $this->Order = $Order; + $this->setErpEntity($ErpEntity); } /** * @param null $Locale - * @param QUI\ERP\Order\AbstractOrder|null $Order + * @param QUI\ERP\ErpEntityInterface|null $ErpEntity * * @return QUI\ERP\Products\Utils\PriceFactor */ public function toPriceFactor( $Locale = null, - QUI\ERP\Order\AbstractOrder $Order = null + QUI\ERP\ErpEntityInterface $ErpEntity = null ): QUI\ERP\Products\Utils\PriceFactor { - if ($Order === null) { - $Order = $this->Order; + if ($ErpEntity === null) { + $ErpEntity = $this->ErpEntity; } $price = $this->getPrice(); - // if order currency is different to the default, we have to convert the price - $OrderCurrency = $Order->getCurrency(); + // if erp entity currency is different to the default, we have to convert the price + $EntityCurrency = $ErpEntity->getCurrency(); $DefaultCurrency = QUI\ERP\Defaults::getCurrency(); - if ($DefaultCurrency->getCode() !== $OrderCurrency->getCode()) { + if ($DefaultCurrency->getCode() !== $EntityCurrency->getCode()) { try { - $price = $DefaultCurrency->convert($price, $OrderCurrency); + $price = $DefaultCurrency->convert($price, $EntityCurrency); } catch (Exception $exception) { } } @@ -797,11 +806,11 @@ public function toPriceFactor( 'basis' => QUI\ERP\Accounting\Calc::CALCULATION_BASIS_CURRENTPRICE, 'value' => $price, 'visible' => true, - 'currency' => $OrderCurrency->getCode() + 'currency' => $EntityCurrency->getCode() ]); $isEuVatUser = QUI\ERP\Tax\Utils::isUserEuVatUser( - $Order->getCustomer() + $ErpEntity->getCustomer() ); if ($isEuVatUser) { @@ -810,7 +819,7 @@ public function toPriceFactor( try { $PriceFactor->setVat( - QUI\ERP\Shipping\Shipping::getInstance()->getOrderVat($Order) + QUI\ERP\Shipping\Shipping::getInstance()->getVat($ErpEntity) ); } catch (QUI\Exception $Exception) { QUI\System\Log::addError($Exception->getMessage()); -- GitLab