Skip to content

Commit

Permalink
feat: add CartInitiated notification
Browse files Browse the repository at this point in the history
  • Loading branch information
Francois-Gomis committed Jan 24, 2025
1 parent 13d91b8 commit 8d49df1
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 19 deletions.
51 changes: 51 additions & 0 deletions Observer/SalesQuoteSaveAfterObserver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace Alma\MonthlyPayments\Observer;


use Alma\MonthlyPayments\Helpers\Logger;
use Alma\MonthlyPayments\Services\MerchantBusinessService;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Quote\Model\Quote;


class SalesQuoteSaveAfterObserver implements ObserverInterface
{
/**
* @var Logger
*/
private $logger;
/**
* @var MerchantBusinessService
*/
private $merchantBusinessService;

/**
* @param Logger $logger
* @param MerchantBusinessService $merchantBusinessService
*/
public function __construct(
Logger $logger,
MerchantBusinessService $merchantBusinessService
)
{
$this->logger = $logger;
$this->merchantBusinessService = $merchantBusinessService;
}

/**
* @param Observer $observer
* @return void
*/
public function execute(Observer $observer): void
{
/** @var Quote $quote */
$quote = $observer->getEvent()->getData('quote');
if (!$this->merchantBusinessService->isSendCartInitiatedNotification($quote)) {
$this->merchantBusinessService->createAndSendCartInitiatedBusinessEvent($quote);
}
}


}
58 changes: 58 additions & 0 deletions Services/MerchantBusinessService.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Alma\MonthlyPayments\Services;


use Alma\API\Entities\DTO\MerchantBusinessEvent\CartInitiatedBusinessEvent;
use Alma\API\Entities\DTO\MerchantBusinessEvent\OrderConfirmedBusinessEvent;
use Alma\API\Exceptions\AlmaException;
use Alma\API\Exceptions\ParametersException;
Expand All @@ -19,6 +20,7 @@
class MerchantBusinessService
{
const QUOTE_BNPL_ELIGIBILITY_KEY = 'alma_bnpl_eligibility';
const CART_INITIATED_STATUS_KEY = 'alma_cart_initiated_status';
/**
* @var AlmaClient
*/
Expand Down Expand Up @@ -49,6 +51,62 @@ public function __construct(
$this->quoteRepository = $quoteRepository;
}

/**
* Get cart initiated notification status in quote DB
*
* @param CartInterface $quote
* @return bool
*/
public function isSendCartInitiatedNotification(CartInterface $quote)
{
return (bool)$quote->getData(self::CART_INITIATED_STATUS_KEY);
}

/**
* Create and send cart initiated business event
* Update db Status
* Never throw Exception
*
* @param CartInterface $quote
* @return void
*/
public function createAndSendCartInitiatedBusinessEvent(CartInterface $quote)
{
try {
$businessEvent = $this->createCartInitiatedBusinessEventByQuote($quote);
$this->almaClient->getDefaultClient()->merchants->sendCartInitiatedBusinessEvent($businessEvent);
$this->saveCartInitiatedIsSendStatus($quote);
} catch (AlmaException $e) {
$this->logger->error('Send CartInitiatedBusinessEvent error : ', [$e->getMessage()]);
}
}

/**
* Create a CartInitiatedBusinessEventDTO
*
* @param CartInterface $quote //ID can be null, it's not in return type
* @return CartInitiatedBusinessEvent
* @throws ParametersException
*/
private function createCartInitiatedBusinessEventByQuote(CartInterface $quote): CartInitiatedBusinessEvent
{
$quoteIdBoolOrNull = $quote->getId() ? (string)($quote->getId()) : null;
return new CartInitiatedBusinessEvent($quoteIdBoolOrNull);

}

/**
* Set alma_cart_initiated to true in quote DB
*
* @param CartInterface $quote
* @return void
*/
private function saveCartInitiatedIsSendStatus(CartInterface $quote): void
{
$quote->setData(self::CART_INITIATED_STATUS_KEY, '1');
$this->quoteRepository->save($quote);
}


/**
* Set alma_bnpl_eligibility to true in quote DB
Expand Down
83 changes: 83 additions & 0 deletions Test/Unit/Observer/SalesQuoteSaveAfterObserverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace Alma\MonthlyPayments\Test\Unit\Observer;

use Alma\MonthlyPayments\Helpers\Logger;
use Alma\MonthlyPayments\Observer\SalesQuoteSaveAfterObserver;
use Alma\MonthlyPayments\Services\MerchantBusinessService;
use Magento\Framework\Event;
use Magento\Framework\Event\Observer;
use Magento\Quote\Model\Quote;
use PHPUnit\Framework\TestCase;

class SalesQuoteSaveAfterObserverTest extends TestCase
{

/**
* @var Logger
*/
private $logger;

/**
* @var MerchantBusinessService
*/
private $merchantBusinessService;
/**
* @var SalesQuoteSaveAfterObserver
*/
private $salesQuoteSaveAfterObserver;

public function setUp(): void
{
$this->logger = $this->createMock(Logger::class);
$this->merchantBusinessService = $this->createMock(MerchantBusinessService::class);
$this->salesQuoteSaveAfterObserver = new SalesQuoteSaveAfterObserver(
$this->logger,
$this->merchantBusinessService
);
}

public function testExecuteCartInitiatedNotificationNotSend()
{
$observer = $this->createMock(Observer::class);
$quote = $this->createMock(Quote::class);
$event = $this->createMock(Event::class);
$event->method('getData')->with('quote')->willReturn($quote);
$observer->method('getEvent')->willReturn($event);
$quote->method('getId')->willReturn(42);
$this->merchantBusinessService
->expects($this->once())
->method('isSendCartInitiatedNotification')
->with($quote)
->willReturn(false);
$this->merchantBusinessService
->expects($this->once())
->method('createAndSendCartInitiatedBusinessEvent')
->with($quote);

$this->salesQuoteSaveAfterObserver->execute($observer);
}

public function testExecuteCartInitiatedNotificationAlreadySend()
{
$observer = $this->createMock(Observer::class);
$quote = $this->createMock(Quote::class);
$event = $this->createMock(Event::class);
$event->method('getData')->with('quote')->willReturn($quote);
$observer->method('getEvent')->willReturn($event);
$quote->method('getId')->willReturn(42);
$this->merchantBusinessService
->expects($this->once())
->method('isSendCartInitiatedNotification')
->with($quote)
->willReturn(true);

$this->merchantBusinessService
->expects($this->never())
->method('createAndSendCartInitiatedBusinessEvent')
->with($quote);

$this->salesQuoteSaveAfterObserver->execute($observer);
}

}
81 changes: 81 additions & 0 deletions Test/Unit/Services/MerchantBusinessServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Alma\API\Client;
use Alma\API\Endpoints\Merchants;
use Alma\API\Entities\DTO\MerchantBusinessEvent\CartInitiatedBusinessEvent;
use Alma\API\Entities\DTO\MerchantBusinessEvent\OrderConfirmedBusinessEvent;
use Alma\API\Exceptions\RequestException;
use Alma\MonthlyPayments\Helpers\AlmaClient;
Expand Down Expand Up @@ -245,4 +246,84 @@ public function testCreateOrderConfirmedThrowExceptionForBadDataTypeInConstruct(
$this->merchantBusinessService->createOrderConfirmedBusinessEventByOrder($orderMock);
}

public function testisSendCartInitiatedNotificationReturnTrueFor1()
{
$quote = $this->createMock(Quote::class);
$quote
->method('getData')
->with('alma_cart_initiated_status')
->willReturn('1');
$this->assertTrue($this->merchantBusinessService->isSendCartInitiatedNotification($quote));
}

public function testisSendCartInitiatedNotificationReturnFalseFor0()
{
$quote = $this->createMock(Quote::class);
$quote
->method('getData')
->with('alma_cart_initiated_status')
->willReturn('0');
$this->assertFalse($this->merchantBusinessService->isSendCartInitiatedNotification($quote));
}

public function testSendCartInitiatedNotThrowErrorForBadDTO()
{
$quote = $this->createMock(Quote::class);
$quote
->method('getId')
->willReturn(null);
$this->merchantEndpoint
->expects($this->never())
->method('sendCartInitiatedBusinessEvent');
$this->quoteRepository
->expects($this->never())
->method('save')
->with($quote);
$this->logger
->expects($this->once())
->method('error');
$this->assertNull($this->merchantBusinessService->createAndSendCartInitiatedBusinessEvent($quote));
}

public function testSendCartInitiatedNotThrowErrorRequestException()
{
$quote = $this->createMock(Quote::class);
$quote
->method('getId')
->willReturn(42);
$this->merchantEndpoint
->expects($this->once())
->method('sendCartInitiatedBusinessEvent')
->willThrowException(new RequestException('Error in send'));
$this->quoteRepository
->expects($this->never())
->method('save')
->with($quote);
$this->logger
->expects($this->once())
->method('error');
$this->assertNull($this->merchantBusinessService->createAndSendCartInitiatedBusinessEvent($quote));
}

public function testSendCartInitiatedCallPhpClientAndSaveDBData()
{
$quote = $this->createMock(Quote::class);
$quote
->expects($this->once())
->method('setData')
->with('alma_cart_initiated_status', '1');
$quote
->method('getId')
->willReturn(42);
$this->merchantEndpoint
->expects($this->once())
->method('sendCartInitiatedBusinessEvent');
$this->quoteRepository
->expects($this->once())
->method('save')
->with($quote);
$this->assertNull($this->merchantBusinessService->createAndSendCartInitiatedBusinessEvent($quote));
}


}
41 changes: 28 additions & 13 deletions etc/db_schema.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="quote" resource="checkout" comment="Alma eligibility for quote">
<column xsi:type="boolean" name="alma_bnpl_eligibility" nullable="true" comment="Quote is eligible to alma BNPL"/>
<column xsi:type="boolean" name="alma_bnpl_eligibility" nullable="true"
comment="Quote is eligible to alma BNPL"/>
<column xsi:type="boolean" name="alma_cart_initiated_status" nullable="false" default="false"
comment="Cart initiated notification status"/>
</table>
<table name="quote_item" resource="checkout" comment="Alma insurance Data in Quote Item">
<column xsi:type="text" name="alma_insurance" nullable="true" comment="Alma insurance"/>
Expand All @@ -12,31 +16,42 @@

<table name="alma_insurance_subscription" resource="checkout" comment="Alma insurance subscriptions">
<column xsi:type="int" name="entity_id" identity="true" comment="Auto increment ID"/>
<column xsi:type="int" name="order_id" padding="10" nullable="false" unsigned="true" comment="Subscription Order id"/>
<column xsi:type="int" name="order_item_id" padding="10" nullable="false" unsigned="true" comment="Order item id"/>
<column xsi:type="int" name="order_id" padding="10" nullable="false" unsigned="true"
comment="Subscription Order id"/>
<column xsi:type="int" name="order_item_id" padding="10" nullable="false" unsigned="true"
comment="Order item id"/>
<column xsi:type="varchar" name="name" nullable="false" length="255" comment="Insurance Name"/>
<column xsi:type="varchar" name="subscription_id" nullable="false" length="255" comment="Alma subscription ID"/>
<column xsi:type="varchar" name="subscription_broker_id" length="255" comment="Subscription broker ID"/>
<column xsi:type="varchar" name="subscription_broker_reference" length="255" comment="Subscription broker reference"/>
<column xsi:type="int" name="subscription_amount" nullable="false" unsigned="true" comment="Alma subscription Price"/>
<column xsi:type="varchar" name="contract_id" nullable="false" length="255" comment="Alma contract ID"/>
<column xsi:type="varchar" name="subscription_broker_reference" length="255"
comment="Subscription broker reference"/>
<column xsi:type="int" name="subscription_amount" nullable="false" unsigned="true"
comment="Alma subscription Price"/>
<column xsi:type="varchar" name="contract_id" nullable="false" length="255" comment="Alma contract ID"/>
<column xsi:type="varchar" name="cms_reference" nullable="false" length="255" comment="cms reference - SKU"/>
<column xsi:type="varchar" name="linked_product_name" nullable="false" length="255" comment="Name of insured product"/>
<column xsi:type="varchar" name="linked_product_name" nullable="false" length="255"
comment="Name of insured product"/>
<column xsi:type="int" name="linked_product_price" nullable="false" comment="Price of insured product"/>
<column xsi:type="varchar" name="subscription_state" nullable="false" length="255" comment="Subscription state"/>
<column xsi:type="varchar" name="subscription_state" nullable="false" length="255"
comment="Subscription state"/>
<column xsi:type="varchar" name="mode" nullable="false" length="10" comment="Subscription mode live/test"/>
<column xsi:type="datetime" name="date_of_cancelation" nullable="true" comment="Cancellation date"/>
<column xsi:type="datetime" name="date_of_cancelation_request" nullable="true" comment="Cancellation date"/>
<column xsi:type="varchar" name="reason_of_cancelation" nullable="true" length="255" comment="Cancellation reason"/>
<column xsi:type="varchar" name="reason_of_cancelation" nullable="true" length="255"
comment="Cancellation reason"/>
<column xsi:type="boolean" name="is_refunded" default="false" comment="Confirm subscription refund"/>
<column xsi:type="varchar" name="callback_url" nullable="false" length="255" comment="Callback url for update"/>
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="entity_id"/>
</constraint>
<constraint xsi:type="foreign" referenceId="ALMA_SUBSCRIPTION_ORDER_ID_SALES_ORDER_ENTITY_ID" table="alma_insurance_subscription" column="order_id" referenceTable="sales_order" referenceColumn="entity_id" onDelete="CASCADE"/>
<constraint xsi:type="foreign" referenceId="ALMA_SUBSCRIPTION_ORDER_ITEM_ID_SALES_ORDER_ITEM_ID" table="alma_insurance_subscription" column="order_item_id" referenceTable="sales_order_item" referenceColumn="item_id" onDelete="CASCADE"/>
<constraint xsi:type="foreign" referenceId="ALMA_SUBSCRIPTION_ORDER_ID_SALES_ORDER_ENTITY_ID"
table="alma_insurance_subscription" column="order_id" referenceTable="sales_order"
referenceColumn="entity_id" onDelete="CASCADE"/>
<constraint xsi:type="foreign" referenceId="ALMA_SUBSCRIPTION_ORDER_ITEM_ID_SALES_ORDER_ITEM_ID"
table="alma_insurance_subscription" column="order_item_id" referenceTable="sales_order_item"
referenceColumn="item_id" onDelete="CASCADE"/>
<index referenceId="SUBSCRIPTION_EXTERNAL_ID" indexType="btree">
<column name="subscription_id" />
<column name="subscription_id"/>
</index>

</table>
Expand Down
Loading

0 comments on commit 8d49df1

Please sign in to comment.