diff --git a/app/code/Magento/Sales/Model/Order/CustomerAssignment.php b/app/code/Magento/Sales/Model/Order/CustomerAssignment.php new file mode 100644 index 0000000000000..b6ae86461c398 --- /dev/null +++ b/app/code/Magento/Sales/Model/Order/CustomerAssignment.php @@ -0,0 +1,58 @@ +eventManager = $eventManager; + $this->orderRepository = $orderRepository; + } + + /** + * @param OrderInterface $order + * @param CustomerInterface $customer + */ + public function execute(OrderInterface $order, CustomerInterface $customer)/*: void*/ + { + $order->setCustomerId($customer->getId()); + $order->setCustomerIsGuest(false); + $this->orderRepository->save($order); + + $this->eventManager->dispatch( + 'sales_order_customer_assign_after', [ + 'order' => $order, + 'customer' => $customer + ] + ); + } +} diff --git a/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php b/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php index f41ea6888264f..aba7d551d5330 100644 --- a/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php +++ b/app/code/Magento/Sales/Observer/AssignOrderToCustomerObserver.php @@ -12,6 +12,7 @@ use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order\CustomerAssignment; /** * Assign order to customer created after issuing guest order. @@ -24,11 +25,22 @@ class AssignOrderToCustomerObserver implements ObserverInterface private $orderRepository; /** + * @var CustomerAssignment + */ + private $customerAssignmentService; + + /** + * AssignOrderToCustomerObserver constructor. + * * @param OrderRepositoryInterface $orderRepository + * @param CustomerAssignment $customerAssignmentService */ - public function __construct(OrderRepositoryInterface $orderRepository) - { + public function __construct( + OrderRepositoryInterface $orderRepository, + CustomerAssignment $customerAssignmentService + ) { $this->orderRepository = $orderRepository; + $this->customerAssignmentService = $customerAssignmentService; } /** @@ -44,11 +56,8 @@ public function execute(Observer $observer) if (array_key_exists('__sales_assign_order_id', $delegateData)) { $orderId = $delegateData['__sales_assign_order_id']; $order = $this->orderRepository->get($orderId); - if (!$order->getCustomerId()) { - //if customer ID wasn't already assigned then assigning. - $order->setCustomerId($customer->getId()); - $order->setCustomerIsGuest(0); - $this->orderRepository->save($order); + if (!$order->getCustomerId() && $customer->getId()) { + $this->customerAssignmentService->execute($order, $customer); } } } diff --git a/app/code/Magento/SalesRule/Observer/AssignCouponDataAfterOrderCustomerAssignObserver.php b/app/code/Magento/SalesRule/Observer/AssignCouponDataAfterOrderCustomerAssignObserver.php new file mode 100644 index 0000000000000..d9699d334ff6a --- /dev/null +++ b/app/code/Magento/SalesRule/Observer/AssignCouponDataAfterOrderCustomerAssignObserver.php @@ -0,0 +1,52 @@ +updateCouponUsages = $updateCouponUsages; + } + + /** + * @inheritDoc + */ + public function execute(Observer $observer) + { + $event = $observer->getEvent(); + /** @var OrderInterface $order */ + $order = $event->getData(self::EVENT_KEY_ORDER); + + if ($order->getCustomerId()) { + $this->updateCouponUsages->execute($order, true); + } + } +} diff --git a/app/code/Magento/SalesRule/etc/events.xml b/app/code/Magento/SalesRule/etc/events.xml index 8261860bbb7ce..eec0da74f619e 100644 --- a/app/code/Magento/SalesRule/etc/events.xml +++ b/app/code/Magento/SalesRule/etc/events.xml @@ -24,4 +24,7 @@ + + + diff --git a/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php new file mode 100644 index 0000000000000..d04423daabdf9 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/SalesRule/Model/Observer/AssignCouponDataAfterOrderCustomerAssignTest.php @@ -0,0 +1,269 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->eventManager = $this->createMock(\Magento\Framework\Event\ManagerInterface::class); + $this->orderRepository = $this->objectManager->get(Magento\Sales\Model\OrderRepository::class); + $this->delegateCustomerService = $this->objectManager->get(Order\OrderCustomerDelegate::class); + $this->customerRepository = $this->objectManager->get(\Magento\Customer\Api\CustomerRepositoryInterface::class); + $this->ruleCustomerFactory = $this->objectManager->get(Magento\SalesRule\Model\Rule\CustomerFactory::class);; + $this->assignCouponToCustomerObserver = $this->objectManager->get( + Magento\SalesRule\Observer\AssignCouponDataAfterOrderCustomerAssignObserver::class + ); + } + + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Sales/_files/order.php + */ + public function testCouponDataHasBeenAssignedTest() + { + $rule = $this->prepareSalesRule(); + $coupon = $this->attachSalesruleCoupon($rule); + + $order = $this->makeOrderWithCouponAsGuest($coupon); + $this->delegateOrderToBeAssigned($order); + + $customer = $this->registerNewCustomer(); + $ruleCustomer = $this->getSalesruleCustomerUsage($customer, $rule); + + // Assert, that rule customer model has been created for specific customer + $this->assertEquals( + $ruleCustomer->getCustomerId(), + $customer->getId() + ); + + // Assert, that customer has increased coupon usage of specific rule + $this->assertEquals( + 1, + $ruleCustomer->getTimesUsed() + ); + } + + /** + * @magentoAppIsolation enabled + * @magentoDataFixture Magento/Sales/_files/order.php + */ + public function testOrderCancelingDecreasesCouponUsages() + { + $rule = $this->prepareSalesRule(); + $coupon = $this->attachSalesruleCoupon($rule); + + $order = $this->makeOrderWithCouponAsGuest($coupon); + $this->delegateOrderToBeAssigned($order); + + $customer = $this->registerNewCustomer(); + + $order->setCustomerId($customer->getId()); + $this->processOrder($order); + + // Should not throw exception as bux is fixed now + $this->cancelOrder($order); + $ruleCustomer = $this->getSalesruleCustomerUsage($customer, $rule); + + // Assert, that rule customer model has been created for specific customer + $this->assertEquals( + $ruleCustomer->getCustomerId(), + $customer->getId() + ); + + // Assert, that customer has increased coupon usage of specific rule + $this->assertEquals( + 0, + $ruleCustomer->getTimesUsed() + ); + + } + + /** + * @param Order $order + * @return \Magento\Sales\Api\Data\OrderInterface + */ + private function processOrder(Order $order) + { + $order->setState(\Magento\Sales\Model\Order::STATE_PROCESSING); + $order->setStatus(\Magento\Sales\Model\Order::STATE_PROCESSING); + return $this->orderRepository->save($order); + } + + /** + * @param Order $order + */ + private function cancelOrder(Order $order) + { + $order->cancel(); + } + + /** + * @param Customer $customer + * @param Rule $rule + * @return Rule\Customer + */ + private function getSalesruleCustomerUsage(Customer $customer, Rule $rule) : Magento\SalesRule\Model\Rule\Customer + { + $ruleCustomer = $this->ruleCustomerFactory->create(); + return $ruleCustomer->loadByCustomerRule($customer->getId(), $rule->getRuleId()); + } + + /** + * @return Rule + */ + private function prepareSalesRule() : Rule + { + /** @var Rule $salesRule */ + $salesRule = $this->objectManager->create(Rule::class); + $salesRule->setData( + [ + 'name' => '15$ fixed discount on whole cart', + 'is_active' => 1, + 'customer_group_ids' => [GroupManagement::NOT_LOGGED_IN_ID], + 'coupon_type' => Rule::COUPON_TYPE_SPECIFIC, + 'conditions' => [ + [ + 'type' => \Magento\SalesRule\Model\Rule\Condition\Address::class, + 'attribute' => 'base_subtotal', + 'operator' => '>', + 'value' => 45, + ], + ], + 'simple_action' => Rule::CART_FIXED_ACTION, + 'discount_amount' => 15, + 'discount_step' => 0, + 'stop_rules_processing' => 1, + 'website_ids' => [ + $this->objectManager->get(StoreManagerInterface::class)->getWebsite()->getId(), + ], + ] + ); + $this->objectManager->get( + \Magento\SalesRule\Model\ResourceModel\Rule::class + )->save($salesRule); + + return $salesRule; + } + + /** + * @param Rule $salesRule + * @return Coupon + */ + private function attachSalesruleCoupon(Rule $salesRule) : Coupon + { + $coupon = $this->objectManager->create(Coupon::class); + $coupon->setRuleId($salesRule->getId()) + ->setCode('CART_FIXED_DISCOUNT_15') + ->setType(0); + + $this->objectManager->get(CouponRepositoryInterface::class)->save($coupon); + + return $coupon; + } + + /** + * @param Coupon $coupon + * @return Order + */ + private function makeOrderWithCouponAsGuest(Coupon $coupon) : Order + { + $order = $this->objectManager->create(\Magento\Sales\Model\Order::class); + $order->loadByIncrementId('100000001') + ->setCustomerIsGuest(true) + ->setCouponCode($coupon->getCode()) + ->setCreatedAt('2014-10-25 10:10:10') + ->setAppliedRuleIds($coupon->getRuleId()) + ->save(); + + return $order; + } + + /** + * @param Order $order + */ + private function delegateOrderToBeAssigned(Order $order) + { + $this->delegateCustomerService->delegateNew($order->getId()); + } + + /** + * @return Customer + * @throws \Magento\Framework\Exception\InputException + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\State\InputMismatchException + */ + private function registerNewCustomer() : Customer + { + $customer = $this->objectManager->create( + \Magento\Customer\Api\Data\CustomerInterface::class + ); + + /** @var Magento\Customer\Api\Data\CustomerInterface $customer */ + $customer->setWebsiteId(1) + ->setEmail('customer@example.com') + ->setGroupId(1) + ->setStoreId(1) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setDefaultBilling(1) + ->setDefaultShipping(1) + ->setTaxvat('12') + ->setGender(0); + + $customer = $this->customerRepository->save($customer, 'password'); + + return $customer; + } +} +