From 75b3d43005107365a1ebc1a83f890a395a83d513 Mon Sep 17 00:00:00 2001 From: Tobias Nilsson Date: Tue, 14 Nov 2023 09:37:28 +0100 Subject: [PATCH] Added conditions to usages --- Block/Adminhtml/Usage/Edit/BackButton.php | 9 +- Block/Adminhtml/Usage/Edit/Conditions.php | 220 ++++++++++++++++++ Block/Catalog/Product/Usage.php | 6 + Controller/Adminhtml/Usage/Edit.php | 19 +- Controller/Adminhtml/Usage/Save.php | 8 + Model/Usage.php | 219 ++++++++++++++++- Setup/UpgradeData.php | 14 ++ etc/module.xml | 2 +- .../devstone_usagecalculator_usage_form.xml | 10 + 9 files changed, 489 insertions(+), 18 deletions(-) create mode 100644 Block/Adminhtml/Usage/Edit/Conditions.php diff --git a/Block/Adminhtml/Usage/Edit/BackButton.php b/Block/Adminhtml/Usage/Edit/BackButton.php index 27af624..bd6009b 100644 --- a/Block/Adminhtml/Usage/Edit/BackButton.php +++ b/Block/Adminhtml/Usage/Edit/BackButton.php @@ -9,6 +9,7 @@ namespace DevStone\UsageCalculator\Block\Adminhtml\Usage\Edit; use Magento\Backend\Block\Widget\Context; +use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface; /** @@ -19,9 +20,9 @@ class BackButton implements ButtonProviderInterface /** * Url Builder * - * @var \Magento\Framework\UrlInterface + * @var UrlInterface */ - protected $urlBuilder; + protected UrlInterface $urlBuilder; /** * Constructor @@ -36,7 +37,7 @@ public function __construct(Context $context) /** * @return array */ - public function getButtonData() + public function getButtonData(): array { return [ 'label' => __('Back'), @@ -51,7 +52,7 @@ public function getButtonData() * * @return string */ - public function getBackUrl() + public function getBackUrl(): string { return $this->urlBuilder->getUrl('*/*/'); } diff --git a/Block/Adminhtml/Usage/Edit/Conditions.php b/Block/Adminhtml/Usage/Edit/Conditions.php new file mode 100644 index 0000000..07bc5e9 --- /dev/null +++ b/Block/Adminhtml/Usage/Edit/Conditions.php @@ -0,0 +1,220 @@ +_rendererFieldset = $rendererFieldset; + $this->_conditions = $conditions; + $this->ruleFactory = $ruleFactory ?: ObjectManager::getInstance() + ->get(\Magento\CatalogRule\Model\RuleFactory::class); + parent::__construct($context, $registry, $formFactory, $data); + } + + /** + * @inheritdoc + * + * @codeCoverageIgnore + */ + public function getTabClass() + { + return null; + } + + /** + * @inheritdoc + */ + public function getTabUrl() + { + return null; + } + + /** + * @inheritdoc + */ + public function isAjaxLoaded() + { + return false; + } + + /** + * @inheritdoc + */ + public function getTabLabel() + { + return __('Conditions'); + } + + /** + * @inheritdoc + */ + public function getTabTitle() + { + return __('Conditions'); + } + + /** + * @inheritdoc + */ + public function canShowTab() + { + return true; + } + + /** + * @inheritdoc + */ + public function isHidden() + { + return false; + } + + /** + * Prepare form before rendering HTML + * + * @return $this + */ + protected function _prepareForm() + { + $model = $this->_coreRegistry->registry(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE); + $form = $this->addTabToForm($model); + $this->setForm($form); + + return parent::_prepareForm(); + } + + /** + * Handles addition of conditions tab to supplied form. + * + * @param Rule $model + * @param string $fieldsetId + * @param string $formName + * @return \Magento\Framework\Data\Form + * @throws \Magento\Framework\Exception\LocalizedException + */ + protected function addTabToForm($model, $fieldsetId = 'conditions_fieldset', $formName = 'devstone_usagecalculator_usage_form') + { + if (!$model) { + $id = $this->getRequest()->getParam('id'); + $model = $this->ruleFactory->create(); + $model->load($id); + } + $conditionsFieldSetId = $model->getConditionsFieldSetId($formName); + $newChildUrl = $this->getUrl( + 'sales_rule/promo_quote/newConditionHtml/form/' . $conditionsFieldSetId, + ['form_namespace' => $formName] + ); + + /** @var \Magento\Framework\Data\Form $form */ + $form = $this->_formFactory->create(); + $form->setHtmlIdPrefix('rule_'); + + $renderer = $this->getLayout()->createBlock(Fieldset::class); + $renderer->setTemplate( + 'Magento_CatalogRule::promo/fieldset.phtml' + )->setNewChildUrl( + $newChildUrl + )->setFieldSetId( + $conditionsFieldSetId + ); + + $fieldset = $form->addFieldset( + $fieldsetId, + [ + 'legend' => __( + 'Apply the rule only if the following conditions are met (leave blank for all products).' + ) + ] + )->setRenderer( + $renderer + ); + $fieldset->addField( + 'conditions', + 'text', + [ + 'name' => 'conditions', + 'label' => __('Conditions'), + 'title' => __('Conditions'), + 'required' => true, + 'data-form-part' => $formName + ] + )->setRule( + $model + )->setRenderer( + $this->_conditions + ); + + $form->setValues($model->getData()); + $this->setConditionFormName($model->getConditions(), $formName); + return $form; + } + + /** + * Handles addition of form name to condition and its conditions. + * + * @param \Magento\Rule\Model\Condition\AbstractCondition $conditions + * @param string $formName + * @return void + */ + private function setConditionFormName(\Magento\Rule\Model\Condition\AbstractCondition $conditions, $formName) + { + $conditions->setFormName($formName); + if ($conditions->getConditions() && is_array($conditions->getConditions())) { + foreach ($conditions->getConditions() as $condition) { + $this->setConditionFormName($condition, $formName); + } + } + } +} diff --git a/Block/Catalog/Product/Usage.php b/Block/Catalog/Product/Usage.php index 133a37d..9619fe6 100644 --- a/Block/Catalog/Product/Usage.php +++ b/Block/Catalog/Product/Usage.php @@ -147,6 +147,12 @@ public function getUsages($category = null): array 'in' )->create(); $customerUsageItems = $this->usageRepository->getList($searchCriteria)->getItems(); + /** @var \DevStone\UsageCalculator\Model\Usage $customerUsageItem */ + foreach ($customerUsageItems as $key => $customerUsageItem) { + if(!$customerUsageItem->getConditions()->validate($this->getProduct())) { + unset($customerUsageItems[$key]); + } + } $items = array_merge_recursive($items, $customerUsageItems); } } diff --git a/Controller/Adminhtml/Usage/Edit.php b/Controller/Adminhtml/Usage/Edit.php index 031f398..47e5c9a 100644 --- a/Controller/Adminhtml/Usage/Edit.php +++ b/Controller/Adminhtml/Usage/Edit.php @@ -67,24 +67,33 @@ public function execute() { // 1. Get ID $id = $this->getRequest()->getParam('entity_id'); - $objectInstance = $this->objectFactory->create(); + $model = $this->objectFactory->create(); + + $this->_coreRegistry->register(\Magento\SalesRule\Model\RegistryConstants::CURRENT_SALES_RULE, $model); + // 2. Initial checking if ($id) { - $objectInstance->load($id); - if (!$objectInstance->getId()) { + $model->load($id); + if (!$model->getId()) { $this->messageManager->addErrorMessage(__('This record no longer exists.')); /** \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); return $resultRedirect->setPath('*/*/'); } + $model->getConditions()->setFormName('devstone_usagecalculator_usage_form'); + $model->getConditions()->getConditions()[0]['form_name'] = 'devstone_usagecalculator_usage_form'; + $form = $model->getConditionsFieldSetId($model->getConditions()->getFormName()); + $model->getConditions()->setJsFormObject( + $model->getConditionsFieldSetId($model->getConditions()->getFormName()) + ); } - + $conditions = $model->getConditions(); // 3. Set entered data if was error when we do save $data = $this->_session->getFormData(true); if (!empty($data)) { - $objectInstance->addData($data); + $model->addData($data); } // 4. Register model to use later in blocks diff --git a/Controller/Adminhtml/Usage/Save.php b/Controller/Adminhtml/Usage/Save.php index b6c0d13..fafe04d 100644 --- a/Controller/Adminhtml/Usage/Save.php +++ b/Controller/Adminhtml/Usage/Save.php @@ -69,6 +69,13 @@ public function execute(): ResultInterface $objectInstance->load($data['entity_id']); $params['entity_id'] = $data['entity_id']; } + + if (isset($data['rule']['conditions'])) { + $data['conditions'] = $data['rule']['conditions']; + } + unset($data['conditions_serialized']); + + $objectInstance->loadPost($data); $objectInstance->addData($data); $objectInstance = $this->helper->initialize($objectInstance); @@ -101,4 +108,5 @@ public function execute(): ResultInterface } return $resultRedirect->setPath('*/*/'); } + } diff --git a/Model/Usage.php b/Model/Usage.php index 446f965..79307c9 100644 --- a/Model/Usage.php +++ b/Model/Usage.php @@ -9,14 +9,32 @@ namespace DevStone\UsageCalculator\Model; +use DevStone\UsageCalculator\Api\Data\UsageCustomOptionInterface; +use DevStone\UsageCalculator\Api\Data\UsageInterface; +use DevStone\UsageCalculator\Model\Usage\Option\ReadHandler; +use DevStone\UsageCalculator\Model\Usage\Option\SaveHandler; +use Magento\CatalogRule\Model\Rule\Condition\CombineFactory; +use Magento\Framework\Api\AttributeValueFactory; +use Magento\Framework\Api\ExtensionAttributesFactory; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Data\FormFactory; use Magento\Framework\DataObject\IdentityInterface; +use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\DateTime\TimezoneInterface; +use Magento\Rule\Model\Action\Collection; +use Magento\Rule\Model\Condition\Combine; /** * Class Usage * @package DevStone\UsageCalculator\Model */ -class Usage extends AbstractModel implements IdentityInterface, \DevStone\UsageCalculator\Api\Data\UsageInterface +class Usage extends \Magento\Rule\Model\AbstractModel implements IdentityInterface, UsageInterface { /** * CMS page cache tag @@ -35,6 +53,22 @@ class Usage extends AbstractModel implements IdentityInterface, \DevStone\UsageC */ protected $_eventPrefix = 'devstone_usagecalculator_usage'; + public function __construct( + protected CombineFactory $combineFactory, + Context $context, + Registry $registry, + FormFactory $formFactory, + TimezoneInterface $localeDate, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [], + ExtensionAttributesFactory $extensionFactory = null, + AttributeValueFactory $customAttributeFactory = null, + Json $serializer = null + ){ + parent::__construct($context, $registry, $formFactory, $localeDate, $resource, $resourceCollection, $data, $extensionFactory, $customAttributeFactory, $serializer); + } + /** * Initialize resource model * @@ -74,7 +108,7 @@ public function saveCollection(array $data) /** * Get all options of usage * - * @return \DevStone\UsageCalculator\Api\Data\UsageCustomOptionInterface[]|null + * @return UsageCustomOptionInterface[]|null */ public function getOptions() { @@ -82,7 +116,7 @@ public function getOptions() } /** - * @param \DevStone\UsageCalculator\Api\Data\UsageCustomOptionInterface[] $options + * @param UsageCustomOptionInterface[] $options * @return $this */ public function setOptions(array $options = null) @@ -98,8 +132,8 @@ public function afterSave() { parent::afterSave(); - $saveHandler = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\DevStone\UsageCalculator\Model\Usage\Option\SaveHandler::class); + $saveHandler = ObjectManager::getInstance() + ->get(SaveHandler::class); $saveHandler->execute($this); } @@ -111,8 +145,8 @@ public function afterLoad() { parent::afterLoad(); - $readHandler = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\DevStone\UsageCalculator\Model\Usage\Option\ReadHandler::class); + $readHandler = ObjectManager::getInstance() + ->get(ReadHandler::class); $readHandler->execute($this); } @@ -120,7 +154,7 @@ public function afterLoad() /** * Set category_id * @param string $categoryId - * @return \DevStone\UsageCalculator\Api\Data\UsageInterface + * @return UsageInterface */ public function setCategoryId($categoryId) { @@ -135,4 +169,173 @@ public function getCategoryId() { return $this->getData('category_id'); } + + + /** + * Prepare data before saving + * + * @return $this + * @throws LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + public function beforeSave() + { + // Serialize conditions + if ($this->getConditions()) { + $con = $this->serializer->serialize($this->getConditions()->asArray()); + $this->setConditionsSerialized($this->serializer->serialize($this->getConditions()->asArray())); + $this->_conditions = null; + } + + + parent::beforeSave(); + return $this; + } + + public function getActions() + { + return null; + } + + /** + * Set rule combine conditions model + * + * @param Combine $conditions + * @return $this + */ + public function setConditions($conditions) + { + $this->_conditions = $conditions; + return $this; + } + + + /** + * Reset rule combine conditions + * + * @param null|Combine $conditions + * @return $this + */ + protected function _resetConditions($conditions = null) + { + if (null === $conditions) { + $conditions = $this->getConditionsInstance(); + } + $conditions->setRule($this)->setId('1')->setPrefix('conditions'); + $this->setConditions($conditions); + + return $this; + } + + /** + * Getter for rule conditions collection + * + * @return Combine + */ + public function getConditionsInstance() + { + return $this->combineFactory->create(); + } + + /** + * Retrieve rule combine conditions model + * + * @return Combine + */ + public function getConditions() + { + if (empty($this->_conditions)) { + $this->_resetConditions(); + } + + // Load rule conditions if it is applicable + if ($this->hasConditionsSerialized()) { + $conditions = $this->getConditionsSerialized(); + if (!empty($conditions)) { + $conditions = $this->serializer->unserialize($conditions); + if (is_array($conditions) && !empty($conditions)) { + $this->_conditions->loadArray($conditions); + } + } + $this->unsConditionsSerialized(); + } + + return $this->_conditions; + } + + + /** + * Initialize rule model data from array + * + * @param array $data + * @return $this + */ + public function loadPost(array $data) + { + $arr = $this->_convertFlatToRecursive($data); + if (isset($arr['conditions'])) { + $con = $this->getConditions(); + $test = $this->getConditions()->setConditions([]); + $this->getConditions()->setConditions([])->loadArray($arr['conditions'][1]); + } + + return $this; + } + + + /** + * Set specified data to current rule. + * Set conditions and actions recursively. + * Convert dates into \DateTime. + * + * @param array $data + * @return array + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + protected function _convertFlatToRecursive(array $data) + { + $arr = []; + foreach ($data as $key => $value) { + if (($key === 'conditions') && is_array($value)) { + foreach ($value as $id => $data) { + $path = explode('--', $id); + $node = & $arr; + for ($i = 0, $l = count($path); $i < $l; $i++) { + if (!isset($node[$key][$path[$i]])) { + $node[$key][$path[$i]] = []; + } + $node = & $node[$key][$path[$i]]; + } + foreach ($data as $k => $v) { + $node[$k] = $v; + } + } + } else { + $this->setData($key, $value); + } + } + + return $arr; + } + + /** + * Get actions field set id. + * + * @param string $formName + * @return string + * @since 100.1.0 + */ + public function getConditionsFieldSetId($formName = '') + { + return $formName . 'rule_conditions_fieldset_' . $this->getId(); + } + + /** + * Getter for rule actions collection instance + * + * @return Collection + */ + public function getActionsInstance(){ + return null; + } } diff --git a/Setup/UpgradeData.php b/Setup/UpgradeData.php index 6bea2f1..ff2b63d 100644 --- a/Setup/UpgradeData.php +++ b/Setup/UpgradeData.php @@ -74,6 +74,20 @@ public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface ); } + if (version_compare($context->getVersion(), '1.0.7') < 0) { + $usageSetup->addAttribute( + 'devstone_usage', + 'conditions_serialized', + [ + 'type' => 'text', + 'label' => 'conditions_serialized', + 'sort_order' => 30, + 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, + 'group' => 'General Information', + ] + ); + } + $usageSetup->installEntities(); $entities = $usageSetup->getDefaultEntities(); foreach ($entities as $entityName => $entity) { diff --git a/etc/module.xml b/etc/module.xml index a9b43b0..0464221 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,5 +1,5 @@ - + diff --git a/view/adminhtml/ui_component/devstone_usagecalculator_usage_form.xml b/view/adminhtml/ui_component/devstone_usagecalculator_usage_form.xml index 3292f1d..e5d13ef 100644 --- a/view/adminhtml/ui_component/devstone_usagecalculator_usage_form.xml +++ b/view/adminhtml/ui_component/devstone_usagecalculator_usage_form.xml @@ -276,5 +276,15 @@ + + + + 180 + + + + DevStone\UsageCalculator\Block\Adminhtml\Usage\Edit\Conditions + +