contacts_events-8.x-1.x-dev/tests/src/Kernel/TeamBookingStateTransitionTest.php
tests/src/Kernel/TeamBookingStateTransitionTest.php
<?php
namespace Drupal\Tests\contacts_events\Kernel;
use Drupal\commerce_order\Entity\Order;
use Drupal\commerce_order\Entity\OrderItem;
use Drupal\commerce_payment\Entity\Payment;
use Drupal\commerce_price\Price;
use Drupal\contacts_events\Entity\Ticket;
use Drupal\contacts_events_teams\Entity\Team;
use Drupal\contacts_events_teams\Entity\TeamApplication;
use Drupal\contacts_events_teams\Entity\TeamInterface;
/**
* Test the booking process team transition events.
*
* @group contacts_events
*/
class TeamBookingStateTransitionTest extends EventTransitionTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'commerce',
'commerce_checkout',
'commerce_order',
'commerce_price',
'commerce_payment',
'commerce_store',
'contacts',
'contacts_events',
'contacts_events_teams',
'ctools',
'datetime',
'datetime_range',
'decoupled_auth',
'entity',
'entity_reference_revisions',
'facets',
'field',
'file',
'image',
'name',
'options',
'profile',
'rest',
'serialization',
'state_machine',
'taxonomy',
'views',
'views_data_export',
];
/**
* The default team.
*
* @var \Drupal\contacts_events_teams\Entity\Team
*/
protected $team;
/**
* {@inheritdoc}
*/
protected function getTransitions() {
return [
'place',
'confirm',
'confirmed_paid_in_full',
'team_app_in_progress',
'team_app_back_to_pending',
'team_payment_undone',
'paid_in_full',
'payment_undone',
'cancel',
];
}
/**
* {@inheritdoc}
*/
protected function setUp() : void {
parent::setUp();
// 8.7.x introduced a change to how entities are retrieved in
// EntityConverter that uses the current user context, but that is not set
// by default in Kernel tests, so set it explicitly now. However, this
// method was only introduced in 8.7.x, so we need to check it exists first.
// If it doesn't, we are <8.7 and therefore don't need it.
// @todo Remove this if https://drupal.org/node/3063236 gets a better fix.
if (method_exists($this, 'setUpCurrentUser')) {
$this->setUpCurrentUser(['uid' => 0]);
}
}
/**
* {@inheritdoc}
*/
protected function cachedSetup() {
parent::cachedSetup();
$this->installEntitySchema('c_events_team');
$this->installEntitySchema('c_events_team_app');
$this->installEntitySchema('contacts_ticket');
$this->installConfig('commerce_payment');
$this->installConfig('contacts_events');
$this->installConfig('contacts_events_teams');
$contacts_events_config = $this->container->get('config.factory')->getEditable('contacts_events.booking_settings');
$contacts_events_config->set('store_id', $this->store->id())->save();
$this->team = Team::create([
'id' => 1,
'name' => 'Other Team 101',
'event' => $this->event->id(),
]);
$this->team->save();
}
/**
* {@inheritdoc}
*/
protected function uncachedSetup() {
parent::uncachedSetup();
$this->team = Team::load(1);
}
/**
* {@inheritdoc}
*/
protected function getEventData() {
return [
'type' => 'default',
'id' => 101,
'title' => 'Other Event 101',
'settings' => [
'teams' => ['enabled' => TeamInterface::STATUS_OPEN],
],
];
}
/**
* Test checks order item transitions for the process of placing an order.
*
* @dataProvider dataOnOrderPlaced
*/
public function testOrderPlacedTransitions($is_team, $ticket_state_before, $order_status_paid, $ticket_state_after, $transition, $additional) {
$user = $this->createUser();
$user->save();
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = $this->container->get('entity_type.manager');
$order_storage = $entity_type_manager->getStorage('commerce_order');
$order_item_storage = $entity_type_manager->getStorage('commerce_order_item');
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = Order::create([
'type' => 'contacts_booking',
'order_id' => 101,
'store_id' => $this->store,
'state' => 'draft',
'event' => $this->event->id(),
]);
$order->save();
// Create the payment before adding line items so we don't trigger state
// changes earlier than desired.
if ($order_status_paid) {
Payment::create([
'type' => 'payment_default',
'payment_gateway' => $this->gateway->id(),
'order_id' => $order->id(),
'state' => 'completed',
'amount' => new Price('10', 'USD'),
])->save();
// Reload the order so we have the update total paid.
$order = $order_storage->loadUnchanged($order->id());
}
$ticket = Ticket::create([
'type' => 'contacts_ticket',
'price' => new Price('10', 'USD'),
'event' => $this->event->id(),
'mapped_price' => [
'booking_window' => 'standard',
'class' => 'standard',
],
'contact' => $user,
]);
if ($is_team) {
$ticket->set('is_team_ticket', TRUE);
$ticket->set('team', $this->team->id());
}
$ticket->save();
// Create our order item.
$order_item = OrderItem::create([
'type' => 'contacts_ticket',
'state' => $ticket_state_before,
'unit_price' => new Price('10', 'USD'),
'mapped_price' => [
'booking_window' => 'standard',
'class' => 'standard',
],
'order_id' => $order,
]);
$order_item->set('purchased_entity', $ticket->id());
$order_item->save();
// Join everything together.
$ticket->set('order_item', $order_item)->save();
$order->addItem($order_item)->save();
// Clear any previous events.
$this->triggeredEvents = [];
$this->triggeredEventsByGroup = [];
// Start transition.
$order = $order_storage->loadUnchanged($order->id());
$order->getState()->applyTransitionById('place');
$order->save();
if (!empty($transition)) {
$this->checkTransitions($transition, $additional);
}
else {
static::assertTrue(!isset($this->triggeredEventsByGroup['contacts_events_order_items']), 'No transitions expected.');
}
// Check new ticket state.
$order_item = $order_item_storage->loadUnchanged($order_item->id());
static::assertEquals($ticket_state_after, $order_item->get('state')->value);
}
/**
* Data provider for testOrderPlacedTransitions.
*/
public function dataOnOrderPlaced() {
// GROUP 1: Ticket in pending status.
$data['non_team_pending_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'pending',
'order_status' => FALSE,
'ticket_state_after' => 'confirmed',
'transition' => 'confirm',
'additional' => [],
];
$data['non_team_pending_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'pending',
'order_status' => TRUE,
'ticket_state_after' => 'paid_in_full',
'transition' => 'confirmed_paid_in_full',
'additional' => ['confirm', 'paid_in_full'],
];
$data['team_pending_not_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'pending',
'order_status' => FALSE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => 'team_app_in_progress',
'additional' => ['confirm'],
];
$data['team_pending_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'pending',
'order_status' => TRUE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => 'team_app_in_progress',
'additional' => ['confirm'],
];
// GROUP 2: Ticket in confirmed status.
$data['non_team_confirmed_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'confirmed',
'order_status' => FALSE,
'ticket_state_after' => 'confirmed',
'transition' => '',
'additional' => [],
];
// We are ignoring non_team_confirmed_paid as it is an invalid scenario.
// If the order is paid in full then the ticket should be as well, not
// confirmed.
// Team tickets should never be in the confirmed state.
// GROUP 3: Ticket in team_app_in_progress status.
// Non team tickets should never be in the team_app_in_progress state.
$data['team_team_app_in_progress_not_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'team_app_in_progress',
'order_status' => FALSE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => '',
'additional' => [],
];
$data['team_team_app_in_progress_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'team_app_in_progress',
'order_status' => TRUE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => '',
'additional' => [],
];
// GROUP 4: Ticket in paid_in_full status.
$data['non_team_paid_in_full_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'paid_in_full',
'order_status' => FALSE,
'ticket_state_after' => 'confirmed',
'transition' => 'payment_undone',
'additional' => [],
];
$data['non_team_paid_in_full_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'paid_in_full',
'order_status' => TRUE,
'ticket_state_after' => 'paid_in_full',
'transition' => '',
'additional' => [],
];
$data['team_paid_in_full_not_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'paid_in_full',
'order_status' => FALSE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => 'team_payment_undone',
'additional' => [],
];
$data['team_paid_in_full_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'paid_in_full',
'order_status' => TRUE,
'ticket_state_after' => 'paid_in_full',
'transition' => '',
'additional' => [],
];
// GROUP 4: Ticket in cancelled status.
$data['non_team_cancelled_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'cancelled',
'order_status' => FALSE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
$data['non_team_cancelled_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'cancelled',
'order_status' => TRUE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
$data['team_cancelled_not_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'cancelled',
'order_status' => FALSE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
$data['team_cancelled_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'cancelled',
'order_status' => TRUE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
return $data;
}
/**
* Test checks order item transitions for the process of placing an order.
*
* @dataProvider dataOnPaymentMade
*/
public function testPaymentMadeTransitions($is_team, $ticket_state_before, $order_status_paid, $ticket_state_after, $transition = NULL, $additional = [], $team_app_approved = FALSE) {
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = $this->container->get('entity_type.manager');
$order_item_storage = $entity_type_manager->getStorage('commerce_order_item');
$ticket = Ticket::create([
'type' => 'contacts_ticket',
'price' => new Price('10', 'USD'),
'event' => $this->event->id(),
'mapped_price' => [
'booking_window' => 'standard',
'class' => 'standard',
],
]);
if ($is_team) {
$ticket->set('is_team_ticket', TRUE);
$ticket->set('team', $this->team->id());
}
$ticket->save();
if ($team_app_approved) {
TeamApplication::create([
'type' => 'standard',
'ticket' => $ticket->id(),
'state' => 'approved',
])->save();
}
$order_item = OrderItem::create([
'type' => 'contacts_ticket',
'state' => $ticket_state_before,
'unit_price' => new Price('10', $this->store->getDefaultCurrencyCode()),
'mapped_price' => [
'booking_window' => 'standard',
'class' => 'standard',
],
]);
$order_item->set('purchased_entity', $ticket->id());
$order_item->save();
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = Order::create([
'type' => 'contacts_booking',
'store_id' => $this->store,
'order_items' => [$order_item],
'state' => 'draft',
'event' => $this->event->id(),
]);
$order->save();
$payment_amount = $order->getTotalPrice();
if (!$order_status_paid) {
$payment_amount = $payment_amount->subtract(new Price('1', $this->store->getDefaultCurrencyCode()));
}
// Clear any previous events.
$this->triggeredEvents = [];
$this->triggeredEventsByGroup = [];
// Make the payment.
Payment::create([
'type' => 'payment_default',
'payment_gateway' => $this->gateway->id(),
'order_id' => $order->id(),
'state' => 'completed',
'amount' => $payment_amount,
])->save();
// Trigger the order update to apply transitions.
$this->container->get('commerce_payment.order_updater')->updateOrder($order, TRUE);
if (!empty($transition)) {
$this->checkTransitions($transition, $additional);
}
else {
static::assertTrue(!isset($this->triggeredEventsByGroup['contacts_events_order_items']));
}
// Check new ticket state.
$order_item = $order_item_storage->loadUnchanged($order_item->id());
static::assertEquals($order_item->get('state')->value, $ticket_state_after);
}
/**
* Data provider for testPaymentMadeTransitions.
*/
public function dataOnPaymentMade() {
// GROUP 1: Ticket in pending status.
$data['non_team_pending_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'pending',
'order_status' => FALSE,
'ticket_state_after' => 'pending',
'transition' => '',
'additional' => [],
];
$data['non_team_pending_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'pending',
'order_status' => TRUE,
'ticket_state_after' => 'pending',
'transition' => '',
'additional' => [],
];
$data['team_pending_not_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'pending',
'order_status' => FALSE,
'ticket_state_after' => 'pending',
'transition' => '',
'additional' => [],
];
$data['team_pending_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'pending',
'order_status' => TRUE,
'ticket_state_after' => 'pending',
'transition' => '',
'additional' => [],
];
// GROUP 2: Ticket in confirmed status.
$data['non_team_confirmed_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'confirmed',
'order_status' => FALSE,
'ticket_state_after' => 'confirmed',
'transition' => '',
'additional' => [],
];
// We are ignoring non_team_confirmed_paid as it is an invalid scenario.
// If the order is paid in full then the ticket should be as well, not
// confirmed.
// Team tickets should never be in the confirmed state.
// GROUP 3: Ticket in team_app_in_progress status.
$data['team_app_in_progress_not_paid_not_approved'] = [
'is_team' => TRUE,
'ticket_state_before' => 'team_app_in_progress',
'order_status' => FALSE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => '',
'additional' => [],
];
$data['team_team_app_in_progress_paid_not_approved'] = [
'is_team' => TRUE,
'ticket_state_before' => 'team_app_in_progress',
'order_status' => TRUE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => '',
'additional' => [],
];
$data['team_team_app_in_progress_not_paid_approved'] = [
'is_team' => TRUE,
'ticket_state_before' => 'team_app_in_progress',
'order_status' => FALSE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => '',
'additional' => [],
'team_app_approved' => TRUE,
];
$data['team_team_app_in_progress_paid_approved'] = [
'is_team' => TRUE,
'ticket_state_before' => 'team_app_in_progress',
'order_status' => TRUE,
'ticket_state_after' => 'paid_in_full',
'transition' => 'paid_in_full',
'additional' => [],
'team_app_approved' => TRUE,
];
// GROUP 4: Ticket in paid_in_full status.
$data['non_team_paid_in_full_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'paid_in_full',
'order_status' => FALSE,
'ticket_state_after' => 'confirmed',
'transition' => 'payment_undone',
'additional' => [],
];
$data['non_team_paid_in_full_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'paid_in_full',
'order_status' => TRUE,
'ticket_state_after' => 'paid_in_full',
'transition' => '',
'additional' => [],
];
$data['team_paid_in_full_not_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'paid_in_full',
'order_status' => FALSE,
'ticket_state_after' => 'team_app_in_progress',
'transition' => 'team_payment_undone',
'additional' => [],
];
$data['team_paid_in_full_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'paid_in_full',
'order_status' => TRUE,
'ticket_state_after' => 'paid_in_full',
'transition' => '',
'additional' => [],
];
// GROUP 5: Ticket in cancelled status.
$data['non_team_cancelled_not_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'cancelled',
'order_status' => FALSE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
$data['non_team_cancelled_paid'] = [
'is_team' => FALSE,
'ticket_state_before' => 'cancelled',
'order_status' => TRUE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
$data['team_cancelled_not_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'cancelled',
'order_status' => FALSE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
$data['team_cancelled_paid'] = [
'is_team' => TRUE,
'ticket_state_before' => 'cancelled',
'order_status' => TRUE,
'ticket_state_after' => 'cancelled',
'transition' => '',
'additional' => [],
];
return $data;
}
/**
* Test checks order item transitions for the process of placing an order.
*
* @dataProvider dataOnCancelTicket
*/
public function testCancelTicketTransitions($trigger_transition, $is_team, $ticket_state_before, $order_status_paid, $ticket_state_after, $transition = NULL, $additional = []) {
/** @var \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager */
$entity_type_manager = $this->container->get('entity_type.manager');
$order_item_storage = $entity_type_manager->getStorage('commerce_order_item');
$ticket_storage = $entity_type_manager->getStorage('contacts_ticket');
$ticket = Ticket::create([
'type' => 'contacts_ticket',
'price' => new Price('10', 'USD'),
'event' => $this->event->id(),
'mapped_price' => [
'booking_window' => 'standard',
'class' => 'standard',
],
]);
if ($is_team) {
$ticket->set('is_team_ticket', TRUE);
$ticket->set('team', $this->team->id());
}
$ticket->save();
$order_item = OrderItem::create([
'type' => 'contacts_ticket',
'state' => $ticket_state_before,
'unit_price' => new Price('10', $this->store->getDefaultCurrencyCode()),
'mapped_price' => [
'booking_window' => 'standard',
'class' => 'standard',
],
]);
$order_item->set('purchased_entity', $ticket->id());
$order_item->save();
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = Order::create([
'type' => 'contacts_booking',
'store_id' => $this->store,
'order_items' => [$order_item],
'state' => 'draft',
'event' => $this->event->id(),
]);
$order->save();
if ($order_status_paid) {
Payment::create([
'type' => 'payment_default',
'payment_gateway' => $this->gateway->id(),
'order_id' => $order->id(),
'state' => 'completed',
'amount' => $order->getTotalPrice(),
])->save();
}
// Clear any previous events.
$this->triggeredEvents = [];
$this->triggeredEventsByGroup = [];
// Start transition.
$order_item = $order_item_storage->loadUnchanged($order_item->id());
$order_item->get('state')->first()->applyTransitionById($trigger_transition);
$order_item->save();
if (!empty($transition)) {
$this->checkTransitions($transition, $additional);
}
else {
static::assertTrue(!isset($this->triggeredEventsByGroup['contacts_events_order_items']));
}
// Check new ticket state.
$order_item = $order_item_storage->loadUnchanged($order_item->id());
static::assertEquals($order_item->get('state')->value, $ticket_state_after);
if ($is_team) {
$ticket = $ticket_storage->loadUnchanged($ticket->id());
static::assertNull($ticket->get('is_team_ticket')->value);
static::assertNull($ticket->get('team')->value);
}
}
/**
* Data provider for testCancelTicketTransitions.
*/
public function dataOnCancelTicket() {
// GROUP 1: Ticket in cancel transition.
$data['non_team_confirmed'] = [
'trigger_transition' => 'cancel',
'is_team' => FALSE,
'ticket_state_before' => 'confirmed',
'order_status' => FALSE,
'ticket_state_after' => 'cancelled',
'transition' => 'cancel',
'additional' => [],
];
$data['non_team_paid_in_full'] = [
'trigger_transition' => 'cancel',
'is_team' => FALSE,
'ticket_state_before' => 'paid_in_full',
'order_status' => TRUE,
'ticket_state_after' => 'cancelled',
'transition' => 'cancel',
'additional' => [],
];
$data['team_paid_in_full'] = [
'trigger_transition' => 'cancel',
'is_team' => TRUE,
'ticket_state_before' => 'paid_in_full',
'order_status' => TRUE,
'ticket_state_after' => 'cancelled',
'transition' => 'cancel',
'additional' => [],
];
// GROUP 2: Ticket in team_app_back_to_pending transition.
$data['team_app_in_progress'] = [
'trigger_transition' => 'team_app_back_to_pending',
'is_team' => TRUE,
'ticket_state_before' => 'team_app_in_progress',
'order_status' => FALSE,
'ticket_state_after' => 'pending',
'transition' => 'team_app_back_to_pending',
'additional' => [],
];
$data['paid_in_full'] = [
'trigger_transition' => 'team_app_back_to_pending',
'is_team' => TRUE,
'ticket_state_before' => 'paid_in_full',
'order_status' => TRUE,
'ticket_state_after' => 'pending',
'transition' => 'team_app_back_to_pending',
'additional' => [],
];
return $data;
}
}
