diff --git a/commerce_pos.routing.yml b/commerce_pos.routing.yml
index 8ea2023..f1a582d 100644
--- a/commerce_pos.routing.yml
+++ b/commerce_pos.routing.yml
@@ -38,3 +38,12 @@ commerce_pos.main:
     _title: 'Point of Sale'
   requirements:
     _permission: 'access commerce pos pages'
+
+commerce_pos.pos_order_item_widget_autocomplete:
+  path: '/commerce_pos/orderItemAutoCompleteHandler/{entity_type}/{view_mode}/{count}'
+  defaults:
+    _controller: '\Drupal\commerce_pos\Controller\PosOrderItemAutoComplete::orderItemAutoCompleteHandler'
+    _format: json
+    _title: 'Pos Order Item Autocomplete'
+  requirements:
+    _permission: 'access content'
diff --git a/config/install/core.entity_form_display.commerce_order.pos.default.yml b/config/install/core.entity_form_display.commerce_order.pos.default.yml
index 982c0a7..45d7bab 100644
--- a/config/install/core.entity_form_display.commerce_order.pos.default.yml
+++ b/config/install/core.entity_form_display.commerce_order.pos.default.yml
@@ -2,33 +2,27 @@ langcode: en
 status: true
 dependencies:
   config:
-    - commerce_order.commerce_order_type.default
-    - field.field.commerce_order.pos.field_order_items
+    - commerce_order.commerce_order_type.pos
+    - field.field.commerce_order.pos.field_cashier
+    - field.field.commerce_order.pos.field_register
+    - field.field.commerce_order.pos.order_items
   module:
     - commerce_order
     - commerce_pos
+    - inline_entity_form
+_core:
+  default_config_hash: HhZKA_IA19mJACTv88yTh3eJ7gPY3sMapJ1qYbed0zs
 id: commerce_order.pos.default
 targetEntityType: commerce_order
 bundle: pos
 mode: default
 content:
-  order_items:
-    type: inline_entity_form_complex
-    weight: 0
-    settings:
-      override_labels: true
-      label_singular: 'order item'
-      label_plural: 'order items'
-      allow_new: true
-      match_operator: CONTAINS
-      allow_existing: false
-      form_mode: default
-    third_party_settings: {  }
   adjustments:
     type: commerce_adjustment_default
     weight: 1
     settings: {  }
     third_party_settings: {  }
+    region: content
   mail:
     type: email_default
     weight: 2
@@ -37,6 +31,16 @@ content:
       size: 60
       placeholder: ''
     third_party_settings: {  }
+  order_items:
+    type: pos_order_item_widget
+    weight: 0
+    settings:
+      size: 60
+      placeholder: 'Enter a product name'
+      num_results: 10
+      purchasable_entity_view_mode: commerce_pos_product_select
+    third_party_settings: {  }
+    region: content
   uid:
     type: inline_entity_form_complex
     weight: 0
@@ -53,6 +57,8 @@ content:
 hidden:
   billing_profile: true
   created: true
+  field_cashier: true
+  field_register: true
   ip_address: true
   order_number: true
   state: true
diff --git a/config/install/core.entity_view_display.commerce_order_item.default.commerce_pos_order_item.yml b/config/install/core.entity_view_display.commerce_order_item.default.commerce_pos_order_item.yml
new file mode 100644
index 0000000..859e20e
--- /dev/null
+++ b/config/install/core.entity_view_display.commerce_order_item.default.commerce_pos_order_item.yml
@@ -0,0 +1,50 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - commerce_order.commerce_order_item_type.default
+    - core.entity_view_mode.commerce_order_item.commerce_pos_order_item
+  enforced:
+    module:
+      - commerce_product
+  module:
+    - commerce_price
+_core:
+  default_config_hash: YvPk7-s7bZEOuxWBbu1G5wo4qc-OOnZpmCwPUdhp40o
+id: commerce_order_item.default.commerce_pos_order_item
+targetEntityType: commerce_order_item
+bundle: default
+mode: commerce_pos_order_item
+content:
+  purchased_entity:
+    type: entity_reference_entity_view
+    weight: 0
+    settings:
+      view_mode: commerce_pos_product_select
+      link: false
+    third_party_settings: {  }
+    label: hidden
+    region: content
+  quantity:
+    type: number_decimal
+    weight: 1
+    settings:
+      thousand_separator: ''
+      decimal_separator: .
+      scale: 2
+      prefix_suffix: true
+    third_party_settings: {  }
+    label: inline
+    region: content
+  total_price:
+    type: commerce_price_default
+    weight: 2
+    settings:
+      strip_trailing_zeroes: false
+      display_currency_code: false
+    third_party_settings: {  }
+    label: inline
+    region: content
+hidden:
+  adjustments: true
+  unit_price: true
diff --git a/config/install/core.entity_view_display.commerce_product_variation.default.commerce_pos_product_select.yml b/config/install/core.entity_view_display.commerce_product_variation.default.commerce_pos_product_select.yml
new file mode 100644
index 0000000..cb766f0
--- /dev/null
+++ b/config/install/core.entity_view_display.commerce_product_variation.default.commerce_pos_product_select.yml
@@ -0,0 +1,42 @@
+langcode: en
+status: true
+dependencies:
+  config:
+    - commerce_product.commerce_product_variation_type.default
+    - core.entity_view_mode.commerce_product_variation.commerce_pos_product_select
+    - field.field.commerce_product_variation.pos.field_upc
+  module:
+    - commerce_price
+id: commerce_product_variation.default.commerce_pos_product_select
+targetEntityType: commerce_product_variation
+bundle: default
+mode: commerce_pos_product_select
+content:
+  price:
+    label: inline
+    type: commerce_price_default
+    weight: 1
+    region: content
+    settings:
+      strip_trailing_zeroes: false
+      display_currency_code: false
+    third_party_settings: {  }
+  sku:
+    type: string
+    weight: 2
+    region: content
+    label: inline
+    settings:
+      link_to_entity: false
+    third_party_settings: {  }
+  title:
+    type: string
+    weight: 0
+    region: content
+    label: hidden
+    settings:
+      link_to_entity: false
+    third_party_settings: {  }
+hidden:
+  field_upc: true
+  product_id: true
diff --git a/config/install/core.entity_view_mode.commerce_order_item.commerce_pos_order_item.yml b/config/install/core.entity_view_mode.commerce_order_item.commerce_pos_order_item.yml
new file mode 100644
index 0000000..430b9b0
--- /dev/null
+++ b/config/install/core.entity_view_mode.commerce_order_item.commerce_pos_order_item.yml
@@ -0,0 +1,9 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - commerce_order
+id: commerce_order_item.commerce_pos_order_item
+label: 'Commerce POS order item'
+targetEntityType: commerce_order_item
+cache: true
diff --git a/config/install/core.entity_view_mode.commerce_product_variation.commerce_pos_product_select.yml b/config/install/core.entity_view_mode.commerce_product_variation.commerce_pos_product_select.yml
new file mode 100644
index 0000000..961084b
--- /dev/null
+++ b/config/install/core.entity_view_mode.commerce_product_variation.commerce_pos_product_select.yml
@@ -0,0 +1,9 @@
+langcode: en
+status: true
+dependencies:
+  module:
+    - commerce_product
+id: commerce_product_variation.commerce_pos_product_select
+label: 'Commerce POS product select'
+targetEntityType: commerce_product_variation
+cache: true
diff --git a/config/install/field.field.commerce_order.pos.field_order_items.yml b/config/install/field.field.commerce_order.pos.order_items.yml
similarity index 100%
rename from config/install/field.field.commerce_order.pos.field_order_items.yml
rename to config/install/field.field.commerce_order.pos.order_items.yml
diff --git a/config/schema/commerce_pos.schema.yml b/config/schema/commerce_pos.schema.yml
new file mode 100644
index 0000000..c6148d9
--- /dev/null
+++ b/config/schema/commerce_pos.schema.yml
@@ -0,0 +1,19 @@
+field.widget.settings.pos_order_item_widget:
+  type: mapping
+  label: 'Commerce POS Order item widget settings'
+  mapping:
+    num_results:
+      type: integer
+      label: 'Number of results to display'
+    order_item_view_mode:
+      type: string
+      label: 'Order item view mode'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+    purchasable_entity_view_mode:
+      type: string
+      label: 'Purchasable entity view mode'
+    size:
+      type: integer
+      label: 'Size'
diff --git a/src/Controller/PosOrderItemAutoComplete.php b/src/Controller/PosOrderItemAutoComplete.php
new file mode 100644
index 0000000..01d5ac8
--- /dev/null
+++ b/src/Controller/PosOrderItemAutoComplete.php
@@ -0,0 +1,129 @@
+<?php
+
+namespace Drupal\commerce_pos\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Render\RendererInterface;
+use Drupal\user\PrivateTempStoreFactory;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\commerce_product\Entity\ProductVariation;
+
+/**
+ * Class PosOrderItemAutoComplete.
+ *
+ * @package Drupal\commerce_pos\Controller
+ */
+class PosOrderItemAutoComplete extends ControllerBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
+   */
+  protected $entityDisplayRepository;
+
+  /**
+   * The tempstore object.
+   *
+   * @var \Drupal\user\SharedTempStore
+   */
+  protected $tempStore;
+
+  /**
+   * The renderer.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs a new POS object.
+   */
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, RendererInterface $renderer) {
+    $this->entityTypeManager = $entity_type_manager;
+    $this->tempStore = $temp_store_factory->get('commerce_pos');
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.manager'),
+      $container->get('user.private_tempstore'),
+      $container->get('renderer')
+    );
+  }
+
+  /**
+   * Order item auto complete handler.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   * @param $entity_type
+   * @param $view_mode
+   * @param $count
+   *
+   * @return object
+   *   The Json object for auto complete suggestions.
+   */
+  public function orderItemAutoCompleteHandler(Request $request, $entity_type, $view_mode, $count) {
+
+    $results = [];
+    if ($input = $request->query->get('q')) {
+      $suggestions = $this->searchQueryString($input, $count);
+      $view_builder = $this->entityTypeManager->getViewBuilder($entity_type);
+      foreach ($suggestions as $key) {
+        $product_variation = ProductVariation::load($key->variation_id);
+        $results[] = [
+          'value' => $product_variation->id(),
+          'label' => $this->renderer->renderPlain($view_builder->view($product_variation, $view_mode, $product_variation->language()->getId())),
+        ];
+      }
+    }
+
+    return new JsonResponse($results);
+  }
+
+  /**
+   * Helper function for searching the product.
+   *
+   * @param string $string
+   *   The Query string.
+   * @param integer $count
+   *   The count of items to return.
+   *
+   * @return mixed
+   *   The query search result.
+   */
+  public function searchQueryString($string, $count) {
+    // Getting the Store ID.
+    $register = $this->tempStore->get('register');
+    if (!empty($register)) {
+      $register = \Drupal::entityTypeManager()->getStorage('commerce_pos_register')->load($register);
+      $store_id = $register->getStoreId();
+    }
+    else {
+      $store_storage = \Drupal::entityTypeManager()->getStorage('commerce_store');
+      $store_id = $store_storage->loadDefault()->id();
+    }
+
+    // @todo convert to entity query? This might be tricky... need to load all
+    // the products for a store and then all their variations?
+    $connection = \Drupal::service('database');
+    $query = $connection->select('commerce_product_variation_field_data', 'cpvd')
+      ->fields('cpvd', ['variation_id', 'product_id', 'title'])
+      ->range(0, $count);
+    $query->join('commerce_product__stores', 'cps', 'cps.entity_id = cpvd.product_id AND cps.stores_target_id = :store_id
+    AND cpvd.title LIKE :string', [':store_id' => $store_id, ':string' => '%' . $string . '%']);
+
+    $result = $query->execute()->fetchAll();
+
+    return $result;
+  }
+
+}
diff --git a/src/Form/POSForm.php b/src/Form/POSForm.php
index 6fc5ff7..2b27d71 100644
--- a/src/Form/POSForm.php
+++ b/src/Form/POSForm.php
@@ -5,7 +5,7 @@ namespace Drupal\commerce_pos\Form;
 use Drupal\commerce_price\Price;
 use Drupal\Core\Entity\ContentEntityForm;
 use Drupal\Core\Form\FormStateInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Url;
 use Drupal\commerce_price\Entity\Currency;
 use Drupal\commerce_order\Entity\Order;
 
@@ -14,18 +14,6 @@ use Drupal\commerce_order\Entity\Order;
  */
 class POSForm extends ContentEntityForm {
 
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity.manager'),
-      $container->get('entity_type.bundle.info'),
-      $container->get('datetime.time'),
-      $container->get('current_user')
-    );
-  }
-
   /**
    * {@inheritdoc}
    */
@@ -40,11 +28,6 @@ class POSForm extends ContentEntityForm {
     $form['#tree'] = TRUE;
     $form['#theme'] = 'commerce_pos_form';
     $form['#attached']['library'][] = 'commerce_pos/form';
-    // @todo not really the right way to do this. I think the
-    // commerce_pos_keypad should override the template to add the library in.
-    if ($this->moduleHandler->moduleExists('commerce_pos_keypad')) {
-      $form['#attached']['library'][] = 'commerce_pos_keypad/keypad';
-    }
 
     $step = $form_state->get('step');
     $step = $step ?: 'order';
@@ -72,10 +55,11 @@ class POSForm extends ContentEntityForm {
     $form = parent::buildForm($form, $form_state);
 
     // Changed must be sent to the client, for later overwrite error checking.
-    $form['changed'] = [
-      '#type' => 'hidden',
-      '#default_value' => $order->getChangedTime(),
-    ];
+    // @todo are we sure?
+//    $form['changed'] = [
+//      '#type' => 'hidden',
+//      '#default_value' => $order->getChangedTime(),
+//    ];
 
     $form['customer'] = [
       '#type' => 'container',
@@ -89,6 +73,14 @@ class POSForm extends ContentEntityForm {
     ];
 
     $form['actions']['submit']['#value'] = t('Add Payment');
+    // Ensure the user is redirected back to this page after deleting an order.
+    if (isset($form['actions']['delete']['#url']) && $form['actions']['delete']['#url'] instanceof Url) {
+      $form['actions']['delete']['#url']->mergeOptions([
+        'query' => [
+          'destination' => Url::fromRoute('commerce_pos.main')->toString()
+        ]
+      ]);
+    }
 
     return $form;
   }
@@ -97,6 +89,11 @@ class POSForm extends ContentEntityForm {
    * Build the payment form, this is the second and final step of a POS order.
    */
   public function buildPaymentForm(array $form, FormStateinterface $form_state) {
+    // @todo not really the right way to do this. I think the
+    // commerce_pos_keypad should override the template to add the library in.
+    if ($this->moduleHandler->moduleExists('commerce_pos_keypad')) {
+      $form['#attached']['library'][] = 'commerce_pos_keypad/keypad';
+    }
     /* @var \Drupal\commerce_order\Entity\Order $order */
     $order = $this->entity;
     $wrapper_id = 'commerce-pos-pay-form-wrapper';
diff --git a/src/Plugin/Field/FieldWidget/PosOrderItemWidget.php b/src/Plugin/Field/FieldWidget/PosOrderItemWidget.php
new file mode 100644
index 0000000..d1cacf9
--- /dev/null
+++ b/src/Plugin/Field/FieldWidget/PosOrderItemWidget.php
@@ -0,0 +1,377 @@
+<?php
+
+namespace Drupal\commerce_pos\Plugin\Field\FieldWidget;
+
+use Drupal\commerce_order\Entity\OrderItem;
+use Drupal\commerce_product\Entity\ProductVariation;
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\WidgetBase;
+use Drupal\Core\Field\WidgetInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Validator\ConstraintViolationInterface;
+
+/**
+ * Plugin implementation of the 'pos_order_item_widget' widget.
+ *
+ * @FieldWidget(
+ *   id = "pos_order_item_widget",
+ *   label = @Translation("Pos order item widget"),
+ *   field_types = {
+ *     "entity_reference"
+ *   },
+ *   multiple_values = true
+ * )
+ */
+class PosOrderItemWidget extends WidgetBase implements WidgetInterface, ContainerFactoryPluginInterface  {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity display repository.
+   *
+   * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
+   */
+  protected $entityDisplayRepository;
+
+  /**
+   * Constructs a EntityReferenceEntityFormatter instance.
+   *
+   * @param string $plugin_id
+   *   The plugin_id for the formatter.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The definition of the field to which the formatter is associated.
+   * @param array $settings
+   *   The formatter settings.
+   * @param array $third_party_settings
+   *   Any third party settings settings.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
+   *   The entity display repository.
+   */
+  public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings,  EntityTypeManagerInterface $entity_type_manager, EntityDisplayRepositoryInterface $entity_display_repository) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
+
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityDisplayRepository = $entity_display_repository;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $plugin_id,
+      $plugin_definition,
+      $configuration['field_definition'],
+      $configuration['settings'],
+      $configuration['third_party_settings'],
+      $container->get('entity_type.manager'),
+      $container->get('entity_display.repository')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    return [
+      'size' => 60,
+      'placeholder' => 'Enter a product name',
+      'num_results' => 10,
+      'purchasable_entity_view_mode' => 'commerce_pos_product_select',
+      'order_item_view_mode' => 'commerce_pos_order_item',
+    ] + parent::defaultSettings();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state) {
+    $elements = [];
+
+    $elements['size'] = [
+      '#type' => 'number',
+      '#title' => t('Size of textfield'),
+      '#default_value' => $this->getSetting('size'),
+      '#required' => TRUE,
+      '#min' => 1,
+    ];
+    $elements['placeholder'] = [
+      '#type' => 'textfield',
+      '#title' => t('Placeholder'),
+      '#default_value' => $this->getSetting('placeholder'),
+      '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
+    ];
+    $elements['num_results'] = [
+      '#type' => 'number',
+      '#title' => t('Number of search results'),
+      '#default_value' => $this->getSetting('num_results'),
+      '#required' => TRUE,
+      '#min' => 1,
+      '#max' => 50,
+    ];
+    $elements['purchasable_entity_view_mode'] = [
+      '#type' => 'select',
+      '#title' => t('Purchasable entity view mode'),
+      '#default_value' => $this->getSetting('purchasable_entity_view_mode'),
+      '#required' => TRUE,
+      // @todo use the "Purchasable entity type" from the order item type.
+      '#options' => $this->entityDisplayRepository->getViewModeOptions('commerce_product_variation'),
+      '#description' => 'This view mode is used for the list of product variations to select from.'
+    ];
+    $elements['order_item_view_mode'] = [
+      '#type' => 'select',
+      '#title' => t('Order item view mode'),
+      '#default_value' => $this->getSetting('order_item_view_mode'),
+      '#required' => TRUE,
+      // @todo use the entity type from the reference field.
+      '#options' => $this->entityDisplayRepository->getViewModeOptions('commerce_order_item'),
+      '#description' => 'This view mode is used list the order items on the form.'
+    ];
+
+    return $elements;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary() {
+    $summary = [];
+
+    $summary[] = t('Textfield size: @size', ['@size' => $this->getSetting('size')]);
+    if (!empty($this->getSetting('placeholder'))) {
+      $summary[] = t('Placeholder: @placeholder', ['@placeholder' => $this->getSetting('placeholder')]);
+    }
+
+    $summary[] = t('Number of results: @num_results', ['@num_results' => $this->getSetting('num_results')]);
+    $summary[] = t('Purchasable entity view mode: @view_mode', ['@view_mode' => $this->getSetting('purchasable_entity_view_mode')]);
+    $summary[] = t('Order item view mode: @view_mode', ['@view_mode' => $this->getSetting('order_item_view_mode')]);
+
+    return $summary;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function extractFormValues(FieldItemListInterface $items, array $form, FormStateInterface $form_state) {
+    // There's nothing to do here the order already has the references. They
+    // have been added by static::addOrderItem().
+    return;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+    if ($form_state->getTriggeringElement()) {
+      $this->processFormSubmission($items, $form, $form_state);
+    }
+    $element = [
+      '#type' => 'fieldset',
+      '#tree' => TRUE,
+      '#description' => $this->fieldDefinition->getDescription(),
+      '#field_title' => $this->fieldDefinition->getLabel(),
+    ] + $element;
+
+    // Make a wrapper for the entire form.
+    // @todo this feels off. There must be a better way.
+    $wrapper_id = Html::getUniqueId(__CLASS__);
+    $form['#prefix'] = '<div id="' . $wrapper_id . '">';
+    $form['#suffix'] = '</div>';
+
+    $element['product_selector'] = [
+      '#type' => 'textfield',
+      '#size' => $this->getSetting('size'),
+      '#placeholder' => $this->getSetting('placeholder'),
+      '#maxlength' => $this->getFieldSetting('max_length'),
+      '#default_value' => NULL,
+      '#autocomplete_route_name' => 'commerce_pos.pos_order_item_widget_autocomplete',
+      '#autocomplete_route_parameters' => [
+        // @todo use the "Purchasable entity type" from the order type.
+        'entity_type' => 'commerce_product_variation',
+        'view_mode' => $this->getSetting('purchasable_entity_view_mode'),
+        'count' => $this->getSetting('num_results'),
+      ],
+      '#ajax' => [
+        'event' => 'autocompleteclose',
+        'callback' => [$this, 'ajaxRefresh'],
+        'wrapper' => $wrapper_id,
+      ]
+    ];
+
+    // Render a list of products that have been added to the order.
+    $referenced_entities = $items->referencedEntities();
+    $element['products'] = [];
+    /** @var \Drupal\commerce_order\Entity\OrderItem $entity */
+    foreach($referenced_entities as $entity) {
+      $product = $entity->getPurchasedEntity();
+      $view_builder = $this->entityTypeManager->getViewBuilder($entity->getEntityTypeId());
+      $element['products'][]['product'] = [
+        $view_builder->view($entity, $this->getSetting('order_item_view_mode'), $entity->language()->getId()),
+        [
+          '#type' => 'button',
+          '#name' => 'adjust_order_add_' . $product->id(),
+          '#value' => '+',
+          '#ajax' => [
+            'callback' => [$this, 'ajaxRefresh'],
+            'wrapper' => $wrapper_id,
+          ],
+          '#product_id' => $product->id(),
+          '#order_item_id' => $entity->id(),
+          '#quantity' => 1,
+        ],
+        [
+          '#type' => 'button',
+          '#value' => '-',
+          '#name' => 'adjust_order_remove_' . $product->id(),
+          '#ajax' => [
+            'callback' => [$this, 'ajaxRefresh'],
+            'wrapper' => $wrapper_id,
+          ],
+          '#product_id' => $product->id(),
+          '#order_item_id' => $entity->id(),
+          '#quantity' => -1,
+        ],
+
+      ];
+    }
+
+    $element['#default_value'] = $items->referencedEntities();
+    return ['target_id' => $element];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function errorElement(array $element, ConstraintViolationInterface $error, array $form, FormStateInterface $form_state) {
+    return isset($element['target_id']) ? $element['target_id'] : FALSE;
+  }
+
+  /**
+   * Process any user input before building the widget.
+   *
+   * @param \Drupal\Core\Field\FieldItemListInterface $items
+   *   Values for this field.
+   * @param array $form
+   *   The form array.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   */
+  protected function processFormSubmission(FieldItemListInterface $items, array &$form, FormStateInterface $form_state) {
+    $trigger_element = $form_state->getTriggeringElement();
+    if ($trigger_element['#name'] === 'order_items[target_id][product_selector]') {
+      $this->addOrderItem($items, $form, $form_state);
+    }
+    elseif (strpos($trigger_element['#name'], 'adjust_order_') === 0) {
+      $this->adjustOrderItem($items, $form, $form_state);
+    }
+  }
+
+  /**
+   * Adjusts an order item quantity.
+   *
+   * @param \Drupal\Core\Field\FieldItemListInterface $items
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   */
+  protected function adjustOrderItem(FieldItemListInterface $items, array &$form, FormStateInterface &$form_state) {
+    $trigger_element = $form_state->getTriggeringElement();
+    $order_item = OrderItem::load($trigger_element['#order_item_id']);
+    $new_quantity = $order_item->getQuantity() + $trigger_element['#quantity'];
+    /** @var \Drupal\commerce_order\Entity\Order $order */
+    $order = $order_item->getOrder();
+    if ($new_quantity > 0) {
+      $order_item->setQuantity($order_item->getQuantity() + $trigger_element['#quantity']);
+      $order_item->save();
+    }
+    else {
+      $order->removeItem($order_item);
+      $order_item->delete();
+    }
+    $order->recalculateTotalPrice()->save();
+
+    $form_object = $form_state->getFormObject();
+    $form_object->setEntity($order);
+  }
+
+  /**
+   * Adds an order item from the autocomplete test field to the order.
+   *
+   * @param \Drupal\Core\Field\FieldItemListInterface $items
+   * @param array $form
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   */
+  protected function addOrderItem(FieldItemListInterface $items, array &$form, FormStateInterface &$form_state) {
+    // Loading the product variation object.
+    $product_variation = ProductVariation::load($form_state->getValue(['order_items', 'target_id', 'product_selector']));
+    // If we've not loaded a product variation then exit doing nothing.
+    if (!$product_variation) {
+      // There's nothing to do.
+      return;
+    }
+
+    // Remove the user input as we no longer need it.
+    $user_input = $form_state->getUserInput();
+    unset($user_input['order_items']['target_id']['product_selector']);
+    $form_state->setUserInput($user_input);
+
+    // If there is already an order item for this variation add to the quantity.
+    $create_new_order_item = TRUE;
+    // For some reason this doesn't work :(
+//    $order = $form_state->getFormObject()->getEntity();
+//    $order = Order::load($order->id());
+    $order = \Drupal::getContainer()->get('commerce_pos.current_order')->get();
+    /** @var \Drupal\commerce_order\Entity\OrderItem $order_item */
+    foreach ($order->getItems() as $order_item) {
+      if ($order_item->getPurchasedEntity()->id() === $product_variation->id()) {
+        $create_new_order_item = FALSE;
+        $order_item
+          ->setQuantity($order_item->getQuantity() + 1)
+          ->save();
+        $order->recalculateTotalPrice();
+        $order->save();
+        break;
+      }
+    }
+    if ($create_new_order_item) {
+      // Create new order Item for adding into existing order.
+      $order_item = OrderItem::create([
+        'type' => 'default',
+        'purchased_entity' => $product_variation,
+        'quantity' => 1,
+        'unit_price' => $product_variation->getPrice(),
+      ]);
+      $order_item->save();
+
+      // Adding the item into the Order.
+      $order->addItem($order_item)->recalculateTotalPrice()->save();
+      $items->appendItem($order_item);
+    }
+
+    $form_object = $form_state->getFormObject();
+    $form_object->setEntity($order);
+  }
+
+  /**
+   * Ajax callback.
+   */
+  public static function ajaxRefresh(&$form, FormStateInterface $form_state) {
+    // Anything on the form might have changed because we're updating the order.
+    return $form;
+  }
+
+}
diff --git a/tests/fixtures/setup_test_pos.php b/tests/fixtures/setup_test_pos.php
new file mode 100644
index 0000000..91c83f4
--- /dev/null
+++ b/tests/fixtures/setup_test_pos.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * Script to set up configure a test POS set up to make testing and development
+ * simple.
+ */
+
+include_once \Drupal::root() . '/core/tests/Drupal/Tests/RandomGeneratorTrait.php';
+# Create a store.
+class storeCreator {
+  /** @todo  */
+  use \Drupal\Tests\RandomGeneratorTrait;
+  use \Drupal\commerce_store\StoreCreationTrait { createStore as public; }
+
+  public function createProductionVariation(array $values = []) {
+    $variation = \Drupal\commerce_product\Entity\ProductVariation::create($values + [
+      'type' => 'default',
+      'sku' => strtolower($this->randomMachineName()),
+      'title' => $this->randomMachineName(),
+      'status' => 1,
+      'price' => new \Drupal\commerce_price\Price(mt_rand(10,100), 'USD'),
+    ]);
+    $variation->save();
+    return $variation;
+  }
+
+  public function createProduct(array $values = []) {
+    $product = \Drupal\commerce_product\Entity\Product::create($values + [
+      'type' => 'default',
+      'title' => $this->randomMachineName(),
+    ]);
+    $product->save();
+    return $product;
+  }
+}
+
+$storeCreator = new storeCreator();
+$test_store = $storeCreator->createStore('POS test store', 'pos_test_store@example.com', 'physical');
+
+$register = \Drupal\commerce_pos\Entity\Register::create([
+  'store_id' => $test_store->id(),
+  'name' => 'Test register',
+  'cash' => new \Drupal\commerce_price\Price('1000.00', 'USD'),
+]);
+$register->save();
+
+$variations = [
+  $storeCreator->createProductionVariation(['title' => 'T-shirt XL']),
+  $storeCreator->createProductionVariation(['title' => 'T-shirt L']),
+  $storeCreator->createProductionVariation(['title' => 'T-shirt M']),
+];
+
+$storeCreator->createProduct(['variations' => $variations, 'title' => 'T-shirt', 'stores' => [$test_store]]);
+
+$variations = [
+  $storeCreator->createProductionVariation(['title' => 'Jumper XL']),
+  $storeCreator->createProductionVariation(['title' => 'Jumper L']),
+  $storeCreator->createProductionVariation(['title' => 'Jumper M']),
+];
+
+$storeCreator->createProduct(['variations' => $variations, 'title' => 'Jumper', 'stores' => [$test_store]]);
diff --git a/tests/src/Functional/CommercePosCreateStoreTrait.php b/tests/src/Functional/CommercePosCreateStoreTrait.php
new file mode 100644
index 0000000..cf5a8a4
--- /dev/null
+++ b/tests/src/Functional/CommercePosCreateStoreTrait.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace Drupal\Tests\commerce_pos\Functional;
+
+use Drupal\commerce_price\Price;
+use Drupal\commerce_product\Entity\Product;
+use Drupal\commerce_product\Entity\ProductVariation;
+use Drupal\commerce_store\StoreCreationTrait;
+use Drupal\Tests\RandomGeneratorTrait;
+
+/**
+ * Helpers methods for tests to setup a store for commerce_pos testing.
+ */
+trait CommercePosCreateStoreTrait {
+  use RandomGeneratorTrait;
+  use StoreCreationTrait;
+
+  /**
+   * Creates a ProductVariation entity.
+   *
+   * @param array $values
+   *   Values to supply ProductVariation::create().
+   *
+   * @return \Drupal\commerce_product\Entity\ProductVariation
+   */
+  protected function createProductionVariation(array $values = []) {
+    $variation = ProductVariation::create($values + [
+        'type' => 'default',
+        'sku' => strtolower($this->randomMachineName()),
+        'title' => $this->randomMachineName(),
+        'status' => 1,
+        'price' => new Price(mt_rand(10,100), 'USD'),
+      ]);
+    $variation->save();
+    return $variation;
+  }
+
+  /**
+   * Creates a Product entity.
+   *
+   * @param array $values
+   *   Values to supply Product::create().
+   *
+   * @return \Drupal\commerce_product\Entity\Product
+   */
+  protected function createProduct(array $values = []) {
+    $product = Product::create($values + [
+        'type' => 'default',
+        'title' => $this->randomMachineName(),
+      ]);
+    $product->save();
+    return $product;
+  }
+
+}
diff --git a/tests/src/FunctionalJavascript/PosFormTest.php b/tests/src/FunctionalJavascript/PosFormTest.php
new file mode 100644
index 0000000..942e293
--- /dev/null
+++ b/tests/src/FunctionalJavascript/PosFormTest.php
@@ -0,0 +1,189 @@
+<?php
+
+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;
+
+/**
+ * Tests the Commerce POS form.
+ *
+ * @group commerce_pos
+ */
+class PosFormTest extends JavascriptTestBase {
+  use CommercePosCreateStoreTrait;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = [
+    'commerce_pos',
+    // @todo commerce_pos has a circular dependency on commerce_pos_keypad
+    'commerce_pos_keypad'
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  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]]);
+
+    // @todo work out the expected permissions to view products etc...
+    //$this->drupalLogin($this->createUser(['access commerce pos pages']));
+    $this->drupalLogin($this->rootUser);
+  }
+
+  /**
+   * Tests adding and removing products from the POS form.
+   */
+  public function testCommercePosForm() {
+    $web_assert = $this->assertSession();
+    $this->drupalGet('admin/commerce/pos');
+    // 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');
+    $this->assertCount(3, $results);
+    // Click on of the auto-complete.
+    $results[0]->click();
+    $web_assert->assertWaitOnAjaxRequest();
+
+    // Assert that the product is listed as expected.
+    $web_assert->pageTextContains('Jumper');
+    $web_assert->pageTextContains('Quantity 1.00');
+    $web_assert->pageTextContains('Price $50.00');
+    $web_assert->pageTextContains('Total price $50.00');
+    $web_assert->pageTextContains('Remaining Balance $50.00');
+
+    // After selecting something from the autocomplete list the value should be blank again
+    $web_assert->fieldValueEquals('order_items[target_id][product_selector]', '');
+
+    // Add another of the same Jumper
+    $autocomplete_field->setValue('Jum');
+    $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), 'p');
+    $web_assert->waitOnAutocomplete();
+    $results = $this->getSession()->getPage()->findAll('css', '.ui-autocomplete li');
+    $this->assertCount(3, $results);
+    // Click on of the auto-complete.
+    $results[0]->click();
+    //$this->click('a#ui-id-7');
+    $web_assert->assertWaitOnAjaxRequest();
+
+    $web_assert->pageTextContains('Jumper');
+    $web_assert->pageTextContains('Quantity 2.00');
+    $web_assert->pageTextContains('Price $50.00');
+    $web_assert->pageTextContains('Total price $100.00');
+    $web_assert->pageTextContains('Remaining Balance $100.00');
+
+    // Add a T-Shirt
+    $autocomplete_field->setValue('T');
+    $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), '-');
+    $web_assert->waitOnAutocomplete();
+    $results = $this->getSession()->getPage()->findAll('css', '.ui-autocomplete li');
+    $this->assertCount(3, $results);
+    // Click on of the auto-complete.
+    $results[0]->click();
+    $web_assert->assertWaitOnAjaxRequest();
+
+    $web_assert->pageTextContains('T-Shirt');
+    $web_assert->pageTextContains('Quantity 1.00');
+    $web_assert->pageTextContains('Price $23.20');
+    $web_assert->pageTextContains('Total price $23.20');
+    $web_assert->pageTextContains('Remaining Balance $123.20');
+
+    // Click on the buttons to add another Jumper.
+    $this->getSession()->getPage()->findButton('adjust_order_add_4')->click();
+    $web_assert->assertWaitOnAjaxRequest();
+    $web_assert->pageTextContains('Jumper');
+    $web_assert->pageTextContains('Quantity 3.00');
+    $web_assert->pageTextContains('Price $50.00');
+    $web_assert->pageTextContains('Total price $150.00');
+    $web_assert->pageTextContains('Remaining Balance $173.20');
+
+    // Click on the buttons to remove all the jumpers.
+    $button = $this->getSession()->getPage()->findButton('adjust_order_remove_4');
+    for ($i = 0; $i <= 2; $i++) {
+      $button->click();
+      $web_assert->assertWaitOnAjaxRequest();
+    }
+    $web_assert->pageTextNotContains('Jumper');
+    $web_assert->pageTextNotContains('Price $50.00');
+    $web_assert->pageTextContains('T-Shirt');
+    $web_assert->pageTextContains('Remaining Balance $23.20');
+
+    // Click on the buttons to remove the T-Shirt.
+    $this->getSession()->getPage()->findButton('adjust_order_remove_1')->click();
+    $web_assert->assertWaitOnAjaxRequest();
+    $web_assert->pageTextNotContains('T-Shirt');
+    $web_assert->pageTextContains('Remaining Balance $0.00');
+  }
+
+  /**
+   * Tests the Widget settings
+   */
+  public function testWidgetSettings() {
+    $web_assert = $this->assertSession();
+
+    // Change some of the settings so we can test.
+    $form_display = EntityFormDisplay::load('commerce_order.pos.default');
+    $settings = $form_display->getComponent('order_items');
+    $settings['settings']['num_results'] = 1;
+    $settings['settings']['placeholder'] = $this->randomString(20);
+    $form_display->setComponent('order_items', $settings)->save();
+
+    // Get to the order form.
+    $this->drupalPostForm('admin/commerce/pos', [], 'Select Register');
+
+    $autocomplete_field = $this->getSession()
+      ->getPage()
+      ->findField('order_items[target_id][product_selector]');
+    $this->assertEquals($settings['settings']['placeholder'], $autocomplete_field->getAttribute('placeholder'));
+
+    // Ensure that the auto-complete only returns 1 value.
+    $this->drupalGet('admin/commerce/pos');
+    $autocomplete_field->setValue('T');
+    $this->getSession()->getDriver()->keyDown($autocomplete_field->getXpath(), '-');
+    $web_assert->waitOnAutocomplete();
+    $results = $this->getSession()->getPage()->findAll('css', '.ui-autocomplete li');
+    $this->assertCount(1, $results);
+  }
+
+}
