commit 470b586c4019c9682a6263c73121382784460c03
Author: Jonathan Sacksick <jonathan.sacksick@gmail.com>
Date:   Sun Jan 8 19:05:08 2017 +0200

    Issue #2841809: Complete the workflow implementation.

diff --git a/commerce_shipping.services.yml b/commerce_shipping.services.yml
index b2cc8fb..2fe2384 100644
--- a/commerce_shipping.services.yml
+++ b/commerce_shipping.services.yml
@@ -1,14 +1,19 @@
 services:
+  commerce_shipping.order_guard:
+    class: Drupal\commerce_shipping\Guard\OrderGuard
+    tags:
+      - { name: state_machine.guard, group: order }
+
   commerce_shipping.referenceable_plugin_types_subscriber:
     class: Drupal\commerce_shipping\EventSubscriber\ReferenceablePluginTypesSubscriber
     tags:
       - { name: event_subscriber }
 
-    commerce_shipping.order_subscriber:
-      class: Drupal\commerce_shipping\EventSubscriber\OrderSubscriber
-      arguments: ['@entity_type.manager', '@entity.query']
-      tags:
-        - { name: event_subscriber }
+  commerce_shipping.order_subscriber:
+    class: Drupal\commerce_shipping\EventSubscriber\OrderSubscriber
+    arguments: ['@entity_type.manager', '@entity.query']
+    tags:
+      - { name: event_subscriber }
 
   plugin.manager.commerce_shipping_method:
     class: Drupal\commerce_shipping\ShippingMethodManager
diff --git a/src/EventSubscriber/OrderSubscriber.php b/src/EventSubscriber/OrderSubscriber.php
index 1d5df0f..c2806e6 100644
--- a/src/EventSubscriber/OrderSubscriber.php
+++ b/src/EventSubscriber/OrderSubscriber.php
@@ -40,7 +40,10 @@ class OrderSubscriber implements EventSubscriberInterface {
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
-    $events = ['commerce_order.cancel.post_transition' => ['onCancel', -100]];
+    $events = [
+      'commerce_order.cancel.post_transition' => ['onCancel', -100],
+      'commerce_order.place.post_transition' => ['onPlaceTransition', -100],
+    ];
     return $events;
   }
 
@@ -64,4 +67,29 @@ class OrderSubscriber implements EventSubscriberInterface {
     }
   }
 
+  /**
+   * Finalize the order's shipments when the order itself is placed.
+   *
+   * @param \Drupal\state_machine\Event\WorkflowTransitionEvent $event
+   *   The transition event.
+   */
+  public function onPlaceTransition(WorkflowTransitionEvent $event) {
+    if ($event->getToState() != 'fulfillment') {
+      return;
+    }
+    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
+    $order = $event->getEntity();
+    $query = $this->entityQuery->condition('order_id', $order->id());
+    $result = $query->execute();
+    if (!empty($result)) {
+      $shipments = $this->shipmentStorage->loadMultiple($result);
+
+      foreach ($shipments as $shipment) {
+        $transition = $shipment->getState()->getWorkflow()->getTransition('finalize');
+        $shipment->getState()->applyTransition($transition);
+        $shipment->save();
+      }
+    }
+  }
+
 }
diff --git a/src/Guard/OrderGuard.php b/src/Guard/OrderGuard.php
new file mode 100644
index 0000000..a9430a8
--- /dev/null
+++ b/src/Guard/OrderGuard.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\commerce_shipping\Guard;
+
+use Drupal\state_machine\Guard\GuardInterface;
+use Drupal\state_machine\Plugin\Workflow\WorkflowInterface;
+use Drupal\state_machine\Plugin\Workflow\WorkflowTransition;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides workflow guards for orders.
+ */
+class OrderGuard implements GuardInterface {
+
+  /**
+   * @inheritdoc
+   */
+  public function allowed(WorkflowTransition $transition, WorkflowInterface $workflow, EntityInterface $entity) {
+    if (!in_array($workflow->getId(), ['order_fulfillment', 'order_fulfillment_validation']) || !in_array($transition->getId(), ['validate', 'fulfill'])) {
+      return;
+    }
+    $query = \Drupal::service('entity.query')->get('commerce_shipment')
+      ->condition('order_id', $entity->id());
+
+    // Query for non ready shipments.
+    if ($transition->getId() == 'validate') {
+      $query->condition('state', 'ready', '!=');
+    }
+    elseif ($transition->getId() == 'fulfill') {
+      $query->condition('state', 'shipped', '!=');
+    }
+
+    // Allow the transition if the query didn't return any result.
+    return empty($query->execute());
+  }
+
+}
