diff --git a/alma/alma.php b/alma/alma.php index 7678ac6d..89a58225 100644 --- a/alma/alma.php +++ b/alma/alma.php @@ -754,4 +754,13 @@ public function hookActionGetProductPropertiesBefore($params) { return $this->runHookController('actionGetProductPropertiesBefore', $params); } + + public function hookActionObjectUpdateAfter($params) + { + $orderFactory = new Alma\PrestaShop\Factories\OrderFactory(); + $clientHelper = new Alma\PrestaShop\Helpers\ClientHelper(); + $carrierFactory = new Alma\PrestaShop\Factories\CarrierFactory(); + $actionObjectUpdateAfter = new Alma\PrestaShop\Controllers\Hook\ActionObjectUpdateAfter($orderFactory, $clientHelper, $carrierFactory); + $actionObjectUpdateAfter->run($params); + } } diff --git a/alma/composer.json b/alma/composer.json index 2b2dc676..94bf843a 100644 --- a/alma/composer.json +++ b/alma/composer.json @@ -11,7 +11,7 @@ }, "require": { "php": "^5.6 || ~7.0 || ~7.1 || ~7.2 || ~7.3 || ~7.4 || ~8.0 || ~8.1", - "alma/alma-php-client": "^2.0.7", + "alma/alma-php-client": "^2.1.0", "ext-json": "*", "ext-openssl": "*", "prestashop/prestashop-accounts-installer": "^v1.0.4", diff --git a/alma/controllers/hook/ActionObjectUpdateAfter.php b/alma/controllers/hook/ActionObjectUpdateAfter.php new file mode 100644 index 00000000..910aee62 --- /dev/null +++ b/alma/controllers/hook/ActionObjectUpdateAfter.php @@ -0,0 +1,192 @@ +orderFactory = $orderFactory; + $this->clientHelper = $clientHelper; + $this->carrierFactory = $carrierFactory; + } + + /** + * Send tracking information to Alma only for Alma Orders + * + * @param $params + * + * @return void + */ + public function run($params) + { + try { + $orderCarrier = $this->checkParamsContainValidOrderCarrierObject($params); + $carrier = $this->carrierFactory->create($orderCarrier->id_carrier); + $order = $this->getAlmaOrderFromOrderCarrierOrderId($orderCarrier->id_order); + $almaPaymentExternalId = $this->getAlmaPaymentExternalId($order); + $almaClient = $this->getAlmaClient(); + $orderExternalId = $this->getOrCreateAlmaOrderExternalId($almaClient, $order, $almaPaymentExternalId); + $almaClient->orders->addTracking($orderExternalId, $carrier->name, $orderCarrier->tracking_number, $carrier->url); + } catch (AlmaActionObjectUpdateException $e) { + return; + } catch (AlmaException $e) { + Logger::instance()->error('[Alma] - Add tracking error: ' . $e->getMessage()); + } + } + + /** + * Check if params Object is set and is an OrderCarrier + * + * @param $params + * + * @return \OrderCarrierCore $orderCarrier + * + * @throws AlmaActionObjectUpdateException + */ + private function checkParamsContainValidOrderCarrierObject($params) + { + if ( + !isset($params['object']) || + !($params['object'] instanceof \OrderCarrierCore) || + $params['object']->tracking_number === '' + ) { + throw new AlmaActionObjectUpdateException('Object is not an OrderCarrier'); + } + + return $params['object']; + } + + /** + * Get Alma Order from OrderCarrier OrderId + * Throw Exception for no Alma Order + * + * @param $orderId + * + * @return \Order + * + * @throws AlmaActionObjectUpdateException + */ + private function getAlmaOrderFromOrderCarrierOrderId($orderId) + { + try { + $order = $this->orderFactory->create($orderId); + } catch (\PrestaShopException $e) { + Logger::instance()->error('[Alma] - PrestaShopException - Impossible to get Order with id :' . $orderId); + throw new AlmaActionObjectUpdateException('Impossible to get Order'); + } + if ($order->module != ConstantsHelper::ALMA_MODULE_NAME) { + throw new AlmaActionObjectUpdateException('Order is not an Alma Order'); + } + + return $order; + } + + /** + * Get Alma Payment External Id from Order + * Throw Exception if no Payment or no Alma Payment External Id in Order + * + * @param \Order $order + * + * @return string + * + * @throws AlmaActionObjectUpdateException + */ + private function getAlmaPaymentExternalId($order) + { + if (empty($order->getOrderPayments())) { + throw new AlmaActionObjectUpdateException('Order is not an Alma Order'); + } + foreach ($order->getOrderPayments() as $orderPayment) { + /** @var \OrderPayment $orderPayment */ + if (isset($orderPayment->transaction_id)) { + return $orderPayment->transaction_id; + } + } + Logger::instance()->error('[Alma] - No Alma Payment External Id in order ' . $order->reference); + throw new AlmaActionObjectUpdateException('No Alma Payment External Id'); + } + + /** + * Get Alma Client or throw Exception + * + * @return Client + * + * @throws AlmaActionObjectUpdateException + */ + private function getAlmaClient() + { + try { + return $this->clientHelper->getAlmaClient(); + } catch (ClientException $e) { + Logger::instance()->error('[Alma] - ClientException - ' . $e->getMessage()); + throw new AlmaActionObjectUpdateException('Impossible to get Alma Client'); + } + } + + /** + * Get or Create Alma Order External Id + * + * @param $almaClient + * @param $order + * @param $almaPaymentExternalId + * + * @return mixed|null + * + * @throws AlmaActionObjectUpdateException + */ + private function getOrCreateAlmaOrderExternalId($almaClient, $order, $almaPaymentExternalId) + { + try { + $almaPayment = $almaClient->payments->fetch($almaPaymentExternalId); + + $orderExternalId = null; + foreach ($almaPayment->orders as $almaOrder) { + if ($order->reference === $almaOrder->getMerchantReference()) { + $orderExternalId = $almaOrder->getExternalId(); + } + } + if (!isset($orderExternalId)) { + $almaOrder = $almaClient->payments->addOrder($almaPaymentExternalId, [ + 'merchant_reference' => $order->reference, + ] + ); + $orderExternalId = $almaOrder->getExternalId(); + } + + return $orderExternalId; + } catch (AlmaException $e) { + Logger::instance()->error('[Alma] - AlmaException ' . $e->getMessage()); + throw new AlmaActionObjectUpdateException('Impossible to get or create Alma Order'); + } + } +} diff --git a/alma/exceptions/AlmaActionObjectUpdateException.php b/alma/exceptions/AlmaActionObjectUpdateException.php new file mode 100644 index 00000000..cba8fdef --- /dev/null +++ b/alma/exceptions/AlmaActionObjectUpdateException.php @@ -0,0 +1,11 @@ + 'all', 'actionAdminControllerInitBefore' => 'all', + 'actionObjectUpdateAfter' => 'all', 'header' => 'all', 'displayHeader' => 'all', 'displayBackOfficeHeader' => 'all', diff --git a/alma/tests/Unit/Controllers/Hook/ActionObjectUpdateAfterTest.php b/alma/tests/Unit/Controllers/Hook/ActionObjectUpdateAfterTest.php new file mode 100644 index 00000000..fac85df5 --- /dev/null +++ b/alma/tests/Unit/Controllers/Hook/ActionObjectUpdateAfterTest.php @@ -0,0 +1,246 @@ +orderFactory = $this->createMock(OrderFactory::class); + $this->carrierFactory = $this->createMock(CarrierFactory::class); + $carrier = $this->createMock(\Carrier::class); + $carrier->name = 'MyCarrierName'; + $carrier->url = 'myurl'; + $this->carrierFactory->method('create')->willReturn($carrier); + $client = $this->createMock(Client::class); + $this->paymentEndpoint = $this->createMock(Payments::class); + $this->orderEndpoint = $this->createMock(OrdersEndpoint::class); + $client->orders = $this->orderEndpoint; + $client->payments = $this->paymentEndpoint; + + $this->clientHelper = $this->createMock(ClientHelper::class); + $this->clientHelper->method('getAlmaClient')->willReturn($client); + + $this->actionObjectUpdateAfter = new ActionObjectUpdateAfter($this->orderFactory, $this->clientHelper, $this->carrierFactory); + } + + public function testInstanceOfObject() + { + $this->assertInstanceOf(ActionObjectUpdateAfter::class, $this->actionObjectUpdateAfter); + } + + /** + * @dataProvider badObjectProvider + * + * @return void + */ + public function testRunWithoutOrderCarrierObjectReturn($params) + { + $this->orderFactory->expects($this->never())->method('create'); + $this->actionObjectUpdateAfter->run($params); + } + + public function testRunWithNonAlmaOrderReturn() + { + $this->orderFactory + ->expects($this->once()) + ->method('create') + ->willReturn(new \Order()); + + $this->actionObjectUpdateAfter->run($this->paramsWithOrderCarrierObject()); + } + + public function testRunWithAlmaOrderWithoutPaymentsReturn() + { + $almaOrder = new \Order(); + $almaOrder->module = ConstantsHelper::ALMA_MODULE_NAME; + $this->orderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($almaOrder); + + $this->actionObjectUpdateAfter->run($this->paramsWithOrderCarrierObject()); + } + + public function testRunWithAlmaOrderWithPaymentsWithoutAlmaExternalIdReturn() + { + $almaOrder = $this->createMock(\Order::class); + $almaOrder->method('getOrderPayments')->willReturn([new \OrderPayment(), new \OrderPayment()]); + $almaOrder->module = ConstantsHelper::ALMA_MODULE_NAME; + $this->orderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($almaOrder); + $this->orderEndpoint->expects($this->never())->method('addTracking'); + $this->actionObjectUpdateAfter->run($this->paramsWithOrderCarrierObject()); + } + + public function testRunWithAlmaPaymentNotFound() + { + $almaOrder = $this->createMock(\Order::class); + $almaPayment = new \OrderPayment(); + $almaPayment->transaction_id = 'payment_123456'; + $almaOrder->method('getOrderPayments')->willReturn([new \OrderPayment(), $almaPayment]); + $almaOrder->module = ConstantsHelper::ALMA_MODULE_NAME; + $this->orderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($almaOrder); + + $this->paymentEndpoint->method('fetch')->willThrowException(new AlmaException('Payment not found')); + + $this->orderEndpoint->expects($this->never())->method('addTracking'); + $this->actionObjectUpdateAfter->run($this->paramsWithOrderCarrierObject()); + } + + public function testRunWithAlmaPaymentWithoutOrderCreateOrderAndSendShipping() + { + $prestashopOrder = $this->createMock(\Order::class); + $prestashopOrder->reference = 'AZERTY'; + $almaPayment = new \OrderPayment(); + $almaPayment->transaction_id = 'payment_123456'; + $prestashopOrder->method('getOrderPayments')->willReturn([new \OrderPayment(), $almaPayment]); + $prestashopOrder->module = ConstantsHelper::ALMA_MODULE_NAME; + $this->orderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($prestashopOrder); + + $this->paymentEndpoint->method('fetch')->willReturn($this->almaPaymentWithoutOrders()); + + $almaOrder = $this->createMock(Order::class); + $almaOrder->method('getExternalId')->willReturn('order_123'); + $this->paymentEndpoint + ->expects($this->once()) + ->method('addOrder') + ->with('payment_123456', ['merchant_reference' => 'AZERTY']) + ->willReturn($almaOrder); + $this->orderEndpoint + ->expects($this->once()) + ->method('addTracking') + ->with('order_123'); + $this->actionObjectUpdateAfter->run($this->paramsWithOrderCarrierObject()); + } + + public function testRunWithAlmaPaymentWithOrderWithBadMerchantReferenceCreateOrderAndSendShipping() + { + $prestashopOrder = $this->createMock(\Order::class); + $prestashopOrder->reference = 'AZERTY'; + $almaPayment = new \OrderPayment(); + $almaPayment->transaction_id = 'payment_123456'; + $prestashopOrder->method('getOrderPayments')->willReturn([new \OrderPayment(), $almaPayment]); + $prestashopOrder->module = ConstantsHelper::ALMA_MODULE_NAME; + $this->orderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($prestashopOrder); + + $almaOrder = $this->createMock(Order::class); + $almaOrder->method('getExternalId')->willReturn('order_123'); + $almaOrder->method('getMerchantReference')->willReturn('YTREZA'); + $almaPayment = $this->almaPaymentWithoutOrders(); + $almaPayment->orders = [$almaOrder]; + $this->paymentEndpoint->method('fetch')->willReturn($almaPayment); + + $this->paymentEndpoint + ->expects($this->once()) + ->method('addOrder') + ->with('payment_123456', ['merchant_reference' => 'AZERTY']) + ->willReturn($almaOrder); + $this->orderEndpoint + ->expects($this->once()) + ->method('addTracking') + ->with('order_123'); + $this->actionObjectUpdateAfter->run($this->paramsWithOrderCarrierObject()); + } + + public function testRunWithAlmaPaymentWithOrdersWithMerchantReferenceNoCreateOrderAndSendShipping() + { + $prestashopOrder = $this->createMock(\Order::class); + $prestashopOrder->reference = 'AZERTY'; + $almaPayment = new \OrderPayment(); + $almaPayment->transaction_id = 'payment_123456'; + $prestashopOrder->method('getOrderPayments')->willReturn([new \OrderPayment(), $almaPayment]); + $prestashopOrder->module = ConstantsHelper::ALMA_MODULE_NAME; + $this->orderFactory + ->expects($this->once()) + ->method('create') + ->willReturn($prestashopOrder); + + $almaOrder = $this->createMock(Order::class); + $almaOrder->method('getExternalId')->willReturn('order_123'); + $almaOrder->method('getMerchantReference')->willReturn('YTREZA'); + + $almaOrder2 = $this->createMock(Order::class); + $almaOrder2->method('getExternalId')->willReturn('order_321'); + $almaOrder2->method('getMerchantReference')->willReturn('AZERTY'); + + $almaPayment = $this->almaPaymentWithoutOrders(); + $almaPayment->orders = [$almaOrder, $almaOrder2]; + $this->paymentEndpoint->method('fetch')->willReturn($almaPayment); + + $this->paymentEndpoint + ->expects($this->never()) + ->method('addOrder'); + $this->orderEndpoint + ->expects($this->once()) + ->method('addTracking') + ->with('order_321', 'MyCarrierName', 'track_1232', 'myurl'); + $this->actionObjectUpdateAfter->run($this->paramsWithOrderCarrierObject()); + } + + /** + * @return array + */ + public function badObjectProvider() + { + $orderCarrier = $this->createMock(\OrderCarrier::class); + $orderCarrier->tracking_number = ''; + + return [ + 'Null object' => [[]], + 'StdClass' => [['object' => new \stdClass()]], + 'Order' => [['object' => new \Order()]], + 'track without tracking number' => [['object' => $orderCarrier]], + ]; + } + + private function paramsWithOrderCarrierObject() + { + $orderCarrier = $this->createMock(\OrderCarrier::class); + $orderCarrier->tracking_number = 'track_1232'; + + return [ + 'object' => $orderCarrier, + ]; + } + + private function almaPaymentWithoutOrders() + { + $almaPayment = $this->createMock(Payment::class); + $almaPayment->orders = []; + + return $almaPayment; + } +}