diff --git a/modules/receipt/commerce_pos_receipt.module b/modules/receipt/commerce_pos_receipt.module
index babbc62..20f5b65 100644
--- a/modules/receipt/commerce_pos_receipt.module
+++ b/modules/receipt/commerce_pos_receipt.module
@@ -53,12 +53,16 @@ function commerce_pos_receipt_theme() {
 function commerce_pos_receipt_entity_operation(EntityInterface $entity) {
   $operations = [];
 
+  /** @var $entity \Drupal\commerce_order\Entity\OrderInterface */
   if ($entity->getEntityTypeId() == 'commerce_order') {
-    $operations['show_receipt'] = [
-      'title' => t('Show receipt'),
-      'url' => Url::fromRoute('commerce_pos_receipt.show', ['commerce_order' => $entity->id()]),
-      'weight' => 50,
-    ];
+    // @see \Drupal\commerce_pos_receipt\Controller\PrintController::checkAccess()
+    if ($entity->getPlacedTime() && \Drupal::currentUser()->hasPermission('administer commerce_order')) {
+      $operations['show_receipt'] = [
+        'title' => t('Show receipt'),
+        'url' => Url::fromRoute('commerce_pos_receipt.show', ['commerce_order' => $entity->id()]),
+        'weight' => 50,
+      ];
+    }
   }
 
   return $operations;
diff --git a/modules/receipt/commerce_pos_receipt.routing.yml b/modules/receipt/commerce_pos_receipt.routing.yml
index 6ab7a60..12e35bb 100644
--- a/modules/receipt/commerce_pos_receipt.routing.yml
+++ b/modules/receipt/commerce_pos_receipt.routing.yml
@@ -3,6 +3,7 @@ commerce_pos_receipt.ajax:
   defaults:
     _controller: '\Drupal\commerce_pos_receipt\Controller\PrintController::ajaxReceipt'
     _title: 'Order Receipt'
+    print_or_email: 'print'
   options:
     parameters:
       commerce_order:
@@ -22,7 +23,7 @@ commerce_pos_receipt.show:
       commerce_order:
         type: 'entity:commerce_order'
   requirements:
-    _permission: 'administer commerce_order'
+    _custom_access: '\Drupal\commerce_pos_receipt\Controller\PrintController::checkAccess'
 
 commerce_pos_receipt.settings:
   path: '/admin/commerce/config/pos/receipt'
diff --git a/modules/receipt/js/commerce_pos_receipt.js b/modules/receipt/js/commerce_pos_receipt.js
index 95d590e..1018b78 100644
--- a/modules/receipt/js/commerce_pos_receipt.js
+++ b/modules/receipt/js/commerce_pos_receipt.js
@@ -26,14 +26,21 @@
    *   XMLHttpRequest status.
    */
   Drupal.AjaxCommands.prototype.printReceipt = function (ajax, response, status) {
-    $(response.content).print({
-      globalStyles: false,
-      stylesheet: drupalSettings.commercePosReceipt.cssUrl,
-      deferred: $.Deferred().done(function(){
-        // Sloppy, should probaby be nice and scalable, but that might never be needed
-        $('.commerce-pos-receipt-button-done').click();
-      })
-    });
+    // If jQuery.print does not exist then this will not be a function. This
+    // can occur if the libraries is not yet downloaded and under test.
+    if (typeof $(response.content).print === "function") {
+      $(response.content).print({
+        globalStyles: false,
+        stylesheet: drupalSettings.commercePosReceipt.cssUrl,
+        deferred: $.Deferred().done(function () {
+          // Sloppy, should probaby be nice and scalable, but that might never be needed
+          $('.commerce-pos-receipt-button-done').click();
+        })
+      });
+    }
+    else {
+      $('.commerce-pos-receipt-button-done').click();
+    }
   };
 
   /**
@@ -43,7 +50,14 @@
   var printed = false;
   Drupal.behaviors.catchSubmits = {
     attach: function (context, settings) {
-      $('.commerce-pos-receipt-button', context).click( function (e) {
+        // If jQuery.print does not exist then this will not be a function. This
+        // can occur if the libraries is not yet downloaded and under test.
+        if (typeof $().print !== "function") {
+          // nothing to do as there is no jQuery.print library.
+          return;
+        }
+
+        $('.commerce-pos-receipt-button', context).click( function (e) {
         // Normally you would use .once() here, but it applies at the end,
         // and because of the chaining nature of our ajax, it doesn't fire until it is too late
         // and we've clicked the button again and we're in a loop
diff --git a/modules/receipt/src/Controller/PrintController.php b/modules/receipt/src/Controller/PrintController.php
index c1000ad..46bed87 100644
--- a/modules/receipt/src/Controller/PrintController.php
+++ b/modules/receipt/src/Controller/PrintController.php
@@ -6,6 +6,7 @@ use Drupal\commerce_order\Entity\OrderInterface;
 use Drupal\commerce_pos_receipt\Ajax\CompleteOrderCommand;
 use Drupal\commerce_pos_receipt\Ajax\PrintReceiptCommand;
 use Drupal\commerce_price\Entity\Currency;
+use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\HtmlCommand;
 use Drupal\Core\Ajax\SettingsCommand;
@@ -193,4 +194,17 @@ class PrintController extends ControllerBase {
     }
   }
 
+  /**
+   * Checks if the receipt should be printable. The order needs to be placed.
+   *
+   * @param \Drupal\commerce_order\Entity\OrderInterface $commerce_order
+   *   The commerce order.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  public function checkAccess(OrderInterface $commerce_order) {
+    return AccessResult::allowedIf($this->currentUser()->hasPermission('administer commerce_order') && $commerce_order->getPlacedTime())->cachePerPermissions()->cacheUntilEntityChanges($commerce_order);
+  }
+
 }
diff --git a/modules/receipt/tests/src/FunctionalJavascript/PosReceiptTest.php b/modules/receipt/tests/src/FunctionalJavascript/PosReceiptTest.php
new file mode 100644
index 0000000..0e7b88b
--- /dev/null
+++ b/modules/receipt/tests/src/FunctionalJavascript/PosReceiptTest.php
@@ -0,0 +1,84 @@
+<?php
+
+namespace Drupal\Tests\commerce_pos_receipt\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
+use Drupal\Tests\commerce_pos\Functional\CommercePosCreateStoreTrait;
+
+/**
+ * @group commerce_pos_receipt
+ */
+class PosReceiptTest extends JavascriptTestBase {
+  use CommercePosCreateStoreTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'commerce_pos_receipt',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->setUpStore();
+    // @todo work out the expected permissions to view products etc...
+    $this->drupalLogin($this->rootUser);
+  }
+
+  /**
+   * Tests receipt function.
+   */
+  public function testReceipt() {
+    $web_assert = $this->assertSession();
+    $this->drupalGet('admin/commerce/pos/main');
+    // There is only one register.
+    $web_assert->fieldValueEquals('register', 1);
+    $web_assert->pageTextContains('Test register');
+    $this->drupalPostForm(NULL, [], 'Select Register');
+
+    // Now we should be able to select order items.
+    $autocomplete_field = $this->getSession()->getPage()->findField('order_items[target_id][product_selector]');
+    $autocomplete_field->setValue('Jum');
+    $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), 'p');
+    $web_assert->waitOnAutocomplete();
+    $results = $this->getSession()->getPage()->findAll('css', '.ui-autocomplete li');
+    // Click on of the auto-complete.
+    $results[0]->click();
+    $web_assert->assertWaitOnAjaxRequest();
+
+    // We now have a product selected and current order is in progress.
+    $this->drupalGet('admin/commerce/orders');
+    $web_assert->elementsCount('css', '.views-view-table tbody tr', 1);
+    $web_assert->pageTextContains('Point of Sale');
+    $web_assert->pageTextContains('Draft');
+    $web_assert->pageTextContains('Edit');
+    $web_assert->pageTextNotContains('Show receipt');
+
+    // Go back to the order
+    $this->drupalGet('admin/commerce/pos/main');
+    // Go to the payment page.
+    $this->click('.commerce-pos input[name="op"]');
+    $this->click('input[name="commerce-pos-pay-keypad-add"]');
+    $this->click('input[name="commerce-pos-finish"]');
+    $this->waitForAjaxToFinish();
+    // We now have a complete order and new draft order.
+    $this->drupalGet('admin/commerce/orders');
+    // The first row has a Show receipt action and the second does not.
+    $web_assert->elementContains('xpath', '//table[contains(@class, "views-view-table")]/tbody/tr[1]', 'Show receipt');
+    $web_assert->elementNotContains('xpath', '//table[contains(@class, "views-view-table")]/tbody/tr[2]', 'Show receipt');
+  }
+
+  /**
+   * Waits for jQuery to become active and animations to complete.
+   */
+  protected function waitForAjaxToFinish() {
+    $condition = "(0 === jQuery.active && 0 === jQuery(':animated').length)";
+    $this->assertJsCondition($condition, 10000);
+  }
+
+}
diff --git a/tests/src/Functional/CommercePosCreateStoreTrait.php b/tests/src/Functional/CommercePosCreateStoreTrait.php
index cf63b9c..ca53817 100644
--- a/tests/src/Functional/CommercePosCreateStoreTrait.php
+++ b/tests/src/Functional/CommercePosCreateStoreTrait.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\commerce_pos\Functional;
 
+use Drupal\commerce_pos\Entity\Register;
 use Drupal\commerce_price\Price;
 use Drupal\commerce_product\Entity\Product;
 use Drupal\commerce_product\Entity\ProductVariation;
@@ -54,4 +55,49 @@ trait CommercePosCreateStoreTrait {
     return $product;
   }
 
+  /**
+   * Creates a store with some products.
+   */
+  protected function setUpStore() {
+    // Initial store set up.
+    $test_store = $this->createStore('POS test store', 'pos_test_store@example.com', 'physical');
+
+    $register = Register::create([
+      'store_id' => $test_store->id(),
+      'name' => 'Test register',
+      'cash' => new Price('1000.00', 'USD'),
+    ]);
+    $register->save();
+
+    $variations = [
+      $this->createProductionVariation([
+        'title' => 'T-shirt XL',
+        'price' => new Price("23.20", 'USD'),
+      ]),
+      $this->createProductionVariation(['title' => 'T-shirt L']),
+      $this->createProductionVariation(['title' => 'T-shirt M']),
+    ];
+
+    $this->createProduct([
+      'variations' => $variations,
+      'title' => 'T-shirt',
+      'stores' => [$test_store],
+    ]);
+
+    $variations = [
+      $this->createProductionVariation([
+        'title' => 'Jumper XL',
+        'price' => new Price("50", 'USD'),
+      ]),
+      $this->createProductionVariation(['title' => 'Jumper L']),
+      $this->createProductionVariation(['title' => 'Jumper M']),
+    ];
+
+    $this->createProduct([
+      'variations' => $variations,
+      'title' => 'Jumper',
+      'stores' => [$test_store],
+    ]);
+  }
+
 }
diff --git a/tests/src/FunctionalJavascript/PosFormTest.php b/tests/src/FunctionalJavascript/PosFormTest.php
index 70c244d..87c26ed 100644
--- a/tests/src/FunctionalJavascript/PosFormTest.php
+++ b/tests/src/FunctionalJavascript/PosFormTest.php
@@ -2,8 +2,6 @@
 
 namespace Drupal\Tests\commerce_pos\FunctionalJavascript;
 
-use Drupal\commerce_pos\Entity\Register;
-use Drupal\commerce_price\Price;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
 use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
 use Drupal\Tests\commerce_pos\Functional\CommercePosCreateStoreTrait;
@@ -30,47 +28,7 @@ class PosFormTest extends JavascriptTestBase {
    */
   protected function setUp() {
     parent::setUp();
-
-    // Initial store set up.
-    $test_store = $this->createStore('POS test store', 'pos_test_store@example.com', 'physical');
-
-    $register = Register::create([
-      'store_id' => $test_store->id(),
-      'name' => 'Test register',
-      'cash' => new Price('1000.00', 'USD'),
-    ]);
-    $register->save();
-
-    $variations = [
-      $this->createProductionVariation([
-        'title' => 'T-shirt XL',
-        'price' => new Price("23.20", 'USD'),
-      ]),
-      $this->createProductionVariation(['title' => 'T-shirt L']),
-      $this->createProductionVariation(['title' => 'T-shirt M']),
-    ];
-
-    $this->createProduct([
-      'variations' => $variations,
-      'title' => 'T-shirt',
-      'stores' => [$test_store],
-    ]);
-
-    $variations = [
-      $this->createProductionVariation([
-        'title' => 'Jumper XL',
-        'price' => new Price("50", 'USD'),
-      ]),
-      $this->createProductionVariation(['title' => 'Jumper L']),
-      $this->createProductionVariation(['title' => 'Jumper M']),
-    ];
-
-    $this->createProduct([
-      'variations' => $variations,
-      'title' => 'Jumper',
-      'stores' => [$test_store],
-    ]);
-
+    $this->setUpStore();
     // @todo work out the expected permissions to view products etc...
     $this->drupalLogin($this->rootUser);
   }
