View source
<?php
namespace Drupal\Tests\commerce_recurring\Kernel;
use Drupal\commerce_order\Entity\OrderInterface;
use Drupal\commerce_payment\Exception\HardDeclineException;
use Drupal\commerce_price\Price;
use Drupal\commerce_recurring\BillingPeriod;
use Drupal\commerce_recurring\Entity\BillingScheduleInterface;
use Drupal\commerce_recurring\Entity\Subscription;
use Drupal\commerce_recurring\Entity\SubscriptionInterface;
use Drupal\Core\Datetime\DrupalDateTime;
class RecurringOrderManagerTest extends RecurringKernelTestBase {
protected $trialSubscription;
protected $activeSubscription;
protected $recurringOrderManager;
public function setUp() : void {
parent::setUp();
$trial_subscription = Subscription::create([
'type' => 'product_variation',
'store_id' => $this->store
->id(),
'billing_schedule' => $this->billingSchedule,
'uid' => $this->user,
'payment_method' => $this->paymentMethod,
'purchased_entity' => $this->variation,
'title' => $this->variation
->getOrderItemTitle(),
'quantity' => '2',
'unit_price' => new Price('20', 'USD'),
'state' => 'trial',
'trial_starts' => strtotime('2019-02-05 00:00'),
]);
$trial_subscription
->save();
$this->trialSubscription = $this
->reloadEntity($trial_subscription);
$active_subscription = Subscription::create([
'type' => 'product_variation',
'store_id' => $this->store
->id(),
'billing_schedule' => $this->billingSchedule,
'uid' => $this->user,
'payment_method' => $this->paymentMethod,
'purchased_entity' => $this->variation,
'title' => $this->variation
->getOrderItemTitle(),
'quantity' => '2',
'unit_price' => new Price('20', 'USD'),
'state' => 'active',
'starts' => strtotime('2019-02-15 00:00:00'),
]);
$active_subscription
->save();
$this->activeSubscription = $this
->reloadEntity($active_subscription);
$this->recurringOrderManager = $this->container
->get('commerce_recurring.order_manager');
}
public function testStartTrialWithInvalidState() {
$this
->expectException(\InvalidArgumentException::class);
$this
->expectExceptionMessage('Unexpected subscription state "active".');
$order = $this->recurringOrderManager
->startTrial($this->activeSubscription);
}
public function testStartTrialWithInvalidBillingSchedule() {
$configuration = $this->billingSchedule
->getPluginConfiguration();
unset($configuration['trial_interval']);
$this->billingSchedule
->setPluginConfiguration($configuration);
$this->billingSchedule
->save();
$this
->expectException(\InvalidArgumentException::class);
$this
->expectExceptionMessage('The billing schedule "test_id" does not allow trials.');
$order = $this->recurringOrderManager
->startTrial($this->trialSubscription);
}
public function testStartTrialPostpaid() {
$order = $this->recurringOrderManager
->startTrial($this->trialSubscription);
$expected_billing_period = new BillingPeriod(new DrupalDateTime('2019-02-05 00:00:00'), new DrupalDateTime('2019-02-15 00:00:00'));
$billing_period_item = $order
->get('billing_period')
->first();
$billing_period = $billing_period_item
->toBillingPeriod();
$this
->assertEquals($expected_billing_period, $billing_period);
$this
->assertTrue($this->trialSubscription
->hasOrder($order));
$this
->assertEmpty($this->trialSubscription
->getRenewedTime());
$this
->assertEmpty($this->trialSubscription
->getNextRenewalTime());
$this
->assertOrder($order, $this->trialSubscription);
$this
->assertTrue($order
->hasItems());
$order_items = $order
->getItems();
$order_item = reset($order_items);
$order_item_billing_period_item = $order_item
->get('billing_period')
->first();
$order_item_billing_period = $order_item_billing_period_item
->toBillingPeriod();
$this
->assertEquals('recurring_product_variation', $order_item
->bundle());
$this
->assertEquals($this->trialSubscription
->id(), $order_item
->get('subscription')->target_id);
$this
->assertEquals($this->trialSubscription
->getTitle(), $order_item
->getTitle());
$this
->assertEquals($this->trialSubscription
->getQuantity(), $order_item
->getQuantity());
$this
->assertEquals($this->trialSubscription
->getPurchasedEntityId(), $order_item
->getPurchasedEntityId());
$this
->assertEquals($billing_period, $order_item_billing_period);
$this
->assertTrue($order_item
->getTotalPrice()
->isZero());
}
public function testStartTrialPrepaid() {
$this->billingSchedule
->setBillingType(BillingScheduleInterface::BILLING_TYPE_PREPAID);
$this->billingSchedule
->save();
$order = $this->recurringOrderManager
->startTrial($this->trialSubscription);
$expected_billing_period = new BillingPeriod(new DrupalDateTime('2019-02-05 00:00:00'), new DrupalDateTime('2019-02-15 00:00:00'));
$billing_period_item = $order
->get('billing_period')
->first();
$billing_period = $billing_period_item
->toBillingPeriod();
$this
->assertEquals($expected_billing_period, $billing_period);
$this
->assertTrue($this->trialSubscription
->hasOrder($order));
$this
->assertEmpty($this->trialSubscription
->getRenewedTime());
$this
->assertEmpty($this->trialSubscription
->getNextRenewalTime());
$this
->assertOrder($order, $this->trialSubscription);
$this
->assertTrue($order
->hasItems());
$order_items = $order
->getItems();
$order_item = reset($order_items);
$order_item_billing_period_item = $order_item
->get('billing_period')
->first();
$order_item_billing_period = $order_item_billing_period_item
->toBillingPeriod();
$expected_order_item_billing_period = new BillingPeriod(new DrupalDateTime('2019-02-15 00:00:00'), new DrupalDateTime('2019-03-01 00:00'));
$this
->assertEquals('recurring_product_variation', $order_item
->bundle());
$this
->assertEquals($this->trialSubscription
->id(), $order_item
->get('subscription')->target_id);
$this
->assertEquals($this->trialSubscription
->getTitle(), $order_item
->getTitle());
$this
->assertEquals($this->trialSubscription
->getQuantity(), $order_item
->getQuantity());
$this
->assertEquals($this->trialSubscription
->getPurchasedEntityId(), $order_item
->getPurchasedEntityId());
$this
->assertEquals($expected_order_item_billing_period, $order_item_billing_period);
$this
->assertEquals($this->trialSubscription
->getUnitPrice()
->divide('2'), $order_item
->getUnitPrice());
}
public function testStartRecurringWithInvalidState() {
$this
->expectException(\InvalidArgumentException::class);
$this
->expectExceptionMessage('Unexpected subscription state "trial".');
$order = $this->recurringOrderManager
->startRecurring($this->trialSubscription);
}
public function testStartRecurringPostpaid() {
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$expected_billing_period = new BillingPeriod(new DrupalDateTime('2019-02-01 00:00:00'), new DrupalDateTime('2019-03-01 00:00'));
$billing_period_item = $order
->get('billing_period')
->first();
$billing_period = $billing_period_item
->toBillingPeriod();
$this
->assertEquals($expected_billing_period, $billing_period);
$this
->assertEquals($billing_period
->getEndDate()
->getTimestamp(), $this->activeSubscription
->getNextRenewalTime());
$this
->assertEquals(2419200, $billing_period
->getDuration());
$this
->assertTrue($this->activeSubscription
->hasOrder($order));
$this
->assertEmpty($this->activeSubscription
->getRenewedTime());
$this
->assertOrder($order, $this->activeSubscription);
$this
->assertTrue($order
->hasItems());
$order_items = $order
->getItems();
$order_item = reset($order_items);
$order_item_billing_period_item = $order_item
->get('billing_period')
->first();
$order_item_billing_period = $order_item_billing_period_item
->toBillingPeriod();
$expected_order_item_billing_period = new BillingPeriod(new DrupalDateTime('2019-02-15 00:00:00'), new DrupalDateTime('2019-03-01 00:00'));
$this
->assertEquals('recurring_product_variation', $order_item
->bundle());
$this
->assertEquals($this->activeSubscription
->id(), $order_item
->get('subscription')->target_id);
$this
->assertEquals($this->activeSubscription
->getTitle(), $order_item
->getTitle());
$this
->assertEquals($this->activeSubscription
->getQuantity(), $order_item
->getQuantity());
$this
->assertEquals($this->activeSubscription
->getPurchasedEntityId(), $order_item
->getPurchasedEntityId());
$this
->assertEquals($expected_order_item_billing_period, $order_item_billing_period);
$this
->assertEquals($this->activeSubscription
->getUnitPrice()
->divide('2'), $order_item
->getUnitPrice());
$this
->assertEquals($this->activeSubscription
->getStartDate(), $order_item_billing_period
->getStartDate());
$this
->assertEquals($billing_period
->getEndDate(), $order_item_billing_period
->getEndDate());
}
public function testStartRecurringPrepaid() {
$this->billingSchedule
->setBillingType(BillingScheduleInterface::BILLING_TYPE_PREPAID);
$this->billingSchedule
->save();
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$expected_billing_period = new BillingPeriod(new DrupalDateTime('2019-02-01 00:00:00'), new DrupalDateTime('2019-03-01 00:00'));
$billing_period_item = $order
->get('billing_period')
->first();
$billing_period = $billing_period_item
->toBillingPeriod();
$this
->assertEquals($expected_billing_period, $billing_period);
$this
->assertEquals($billing_period
->getEndDate()
->getTimestamp(), $this->activeSubscription
->getNextRenewalTime());
$this
->assertEquals(2419200, $billing_period
->getDuration());
$this
->assertTrue($this->activeSubscription
->hasOrder($order));
$this
->assertEmpty($this->activeSubscription
->getRenewedTime());
$this
->assertOrder($order, $this->activeSubscription);
$this
->assertTrue($order
->hasItems());
$order_items = $order
->getItems();
$order_item = reset($order_items);
$order_item_billing_period_item = $order_item
->get('billing_period')
->first();
$order_item_billing_period = $order_item_billing_period_item
->toBillingPeriod();
$expected_order_item_billing_period = new BillingPeriod(new DrupalDateTime('2019-03-01 00:00:00'), new DrupalDateTime('2019-04-01 00:00'));
$this
->assertEquals('recurring_product_variation', $order_item
->bundle());
$this
->assertEquals($this->activeSubscription
->id(), $order_item
->get('subscription')->target_id);
$this
->assertEquals($this->activeSubscription
->getTitle(), $order_item
->getTitle());
$this
->assertEquals($this->activeSubscription
->getQuantity(), $order_item
->getQuantity());
$this
->assertEquals($this->activeSubscription
->getPurchasedEntityId(), $order_item
->getPurchasedEntityId());
$this
->assertEquals($expected_order_item_billing_period, $order_item_billing_period);
$this
->assertEquals($this->activeSubscription
->getUnitPrice(), $order_item
->getUnitPrice());
}
public function testRefreshOrder() {
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$order_items = $order
->getItems();
$order_item = reset($order_items);
$previous_order_item_id = $order_item
->id();
$this->activeSubscription
->set('payment_method', NULL);
$this->activeSubscription
->setUnitPrice(new Price('3', 'USD'));
$this->activeSubscription
->save();
$this->recurringOrderManager
->refreshOrder($order);
$this
->assertEmpty($order
->get('billing_profile')->target_id);
$this
->assertEmpty($order
->get('payment_method')->target_id);
$this
->assertEmpty($order
->get('payment_gateway')->target_id);
$order_items = $order
->getItems();
$order_item = reset($order_items);
$this
->assertEquals($previous_order_item_id, $order_item
->id());
$this
->assertEquals($this->activeSubscription
->getUnitPrice()
->divide('2'), $order_item
->getUnitPrice());
$this->billingSchedule
->setBillingType(BillingScheduleInterface::BILLING_TYPE_PREPAID);
$this->billingSchedule
->save();
$this->activeSubscription = $this
->reloadEntity($this->activeSubscription);
$this->activeSubscription
->cancel();
$this->activeSubscription
->save();
$this
->reloadEntity($order_item);
$this->recurringOrderManager
->refreshOrder($order);
$this
->assertEquals('canceled', $order
->getState()
->getId());
$this
->assertEmpty($order
->getItems());
}
public function testCloseOrderWithoutPaymentMethod() {
$this->activeSubscription
->set('payment_method', NULL);
$this->activeSubscription
->save();
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$this
->expectException(HardDeclineException::class);
$this
->expectExceptionMessage('Payment method not found.');
$this->recurringOrderManager
->closeOrder($order);
}
public function testCloseOrder() {
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$this->recurringOrderManager
->closeOrder($order);
$this
->assertEquals('completed', $order
->getState()
->getId());
$payment_storage = $this->container
->get('entity_type.manager')
->getStorage('commerce_payment');
$payments = $payment_storage
->loadMultipleByOrder($order);
$this
->assertCount(1, $payments);
$payment = reset($payments);
$this
->assertEquals('completed', $payment
->getState()
->getId());
$this
->assertEquals($this->paymentGateway
->id(), $payment
->getPaymentGatewayId());
$this
->assertEquals($this->paymentMethod
->id(), $payment
->getPaymentMethodId());
$this
->assertEquals($order
->id(), $payment
->getOrderId());
$this
->assertEquals($order
->getTotalPrice(), $payment
->getAmount());
}
public function testCloseOrderAlreadyPaidMarksTheOrderAsCompleted() {
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$order
->set('total_paid', $order
->getTotalPrice())
->set('state', 'needs_payment')
->save();
$order = $this
->reloadEntity($order);
$this
->assertEquals('needs_payment', $order
->getState()
->getId());
$this
->assertTrue($order
->isPaid());
$this->recurringOrderManager
->closeOrder($order);
$order = $this
->reloadEntity($order);
$this
->assertTrue($order
->isPaid());
$this
->assertEquals('completed', $order
->getState()
->getId());
$payment_storage = $this->container
->get('entity_type.manager')
->getStorage('commerce_payment');
$payments = $payment_storage
->loadMultipleByOrder($order);
$this
->assertCount(0, $payments);
}
public function testCloseOrderAlreadyCanceledDoesntAddExtraPayment() {
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$order
->set('state', 'canceled')
->save();
$order = $this
->reloadEntity($order);
$this
->assertEquals('canceled', $order
->getState()
->getId());
$this->recurringOrderManager
->closeOrder($order);
$order = $this
->reloadEntity($order);
$this
->assertFalse($order
->isPaid());
$this
->assertEquals('canceled', $order
->getState()
->getId());
$payment_storage = $this->container
->get('entity_type.manager')
->getStorage('commerce_payment');
$payments = $payment_storage
->loadMultipleByOrder($order);
$this
->assertCount(0, $payments);
}
public function testRenewOrder() {
$order = $this->recurringOrderManager
->startRecurring($this->activeSubscription);
$next_order = $this->recurringOrderManager
->renewOrder($order);
$billing_period_item = $order
->get('billing_period')
->first();
$billing_period = $billing_period_item
->toBillingPeriod();
$next_billing_period_item = $next_order
->get('billing_period')
->first();
$next_billing_period = $next_billing_period_item
->toBillingPeriod();
$this->activeSubscription = $this
->reloadEntity($this->activeSubscription);
$this
->assertTrue($this->activeSubscription
->hasOrder($order));
$this
->assertTrue($this->activeSubscription
->hasOrder($next_order));
$this
->assertNotEmpty($this->activeSubscription
->getRenewedTime());
$this
->assertEquals($next_billing_period
->getEndDate()
->getTimestamp(), $this->activeSubscription
->getNextRenewalTime());
$this
->assertEquals($billing_period
->getEndDate(), $next_billing_period
->getStartDate());
$this
->assertOrder($next_order, $this->activeSubscription);
$this
->assertTrue($next_order
->hasItems());
$order_items = $next_order
->getItems();
$order_item = reset($order_items);
$this
->assertEquals('recurring_product_variation', $order_item
->bundle());
$this
->assertEquals($this->activeSubscription
->getTitle(), $order_item
->getTitle());
$this
->assertEquals($this->activeSubscription
->getQuantity(), $order_item
->getQuantity());
$this
->assertEquals($this->activeSubscription
->getUnitPrice(), $order_item
->getUnitPrice());
$this
->assertEquals($this->variation, $order_item
->getPurchasedEntity());
$this
->assertEquals($next_billing_period, $order_item
->get('billing_period')
->first()
->toBillingPeriod());
$this
->assertEquals(2678400, $next_billing_period
->getDuration());
$this
->assertEquals($this->activeSubscription
->id(), $order_item
->get('subscription')->target_id);
$this->activeSubscription
->cancel(FALSE)
->save();
$result = $this->recurringOrderManager
->renewOrder($next_order);
$this
->assertNull($result);
}
protected function assertOrder(OrderInterface $order, SubscriptionInterface $subscription) {
$this
->assertEquals('recurring', $order
->bundle());
$this
->assertEquals('draft', $order
->getState()
->getId());
$this
->assertEquals($subscription
->getStoreId(), $order
->getStoreId());
$this
->assertEquals($subscription
->getCustomerId(), $order
->getCustomerId());
$this
->assertEquals($subscription
->getBillingSchedule()
->id(), $order
->get('billing_schedule')->target_id);
$payment_method = $subscription
->getPaymentMethod();
$this
->assertEquals($payment_method
->id(), $order
->get('payment_method')->target_id);
$this
->assertEquals($payment_method
->getPaymentGatewayId(), $order
->get('payment_gateway')->target_id);
$this
->assertEquals($payment_method
->getBillingProfile(), $order
->getBillingProfile());
}
}