diff --git a/modules/promotion/src/Plugin/Commerce/PromotionCondition/OrderTotalPrice.php b/modules/promotion/src/Plugin/Commerce/PromotionCondition/OrderTotalPrice.php
index 0dc134b..e39b26b 100644
--- a/modules/promotion/src/Plugin/Commerce/PromotionCondition/OrderTotalPrice.php
+++ b/modules/promotion/src/Plugin/Commerce/PromotionCondition/OrderTotalPrice.php
@@ -22,7 +22,6 @@ class OrderTotalPrice extends PromotionConditionBase {
   public function defaultConfiguration() {
     return [
       'amount' => NULL,
-      // @todo expose the operator in form.
       'operator' => '>',
     ] + parent::defaultConfiguration();
   }
@@ -39,6 +38,21 @@ class OrderTotalPrice extends PromotionConditionBase {
       $amount = NULL;
     }
 
+    $operator = isset($this->configuration['operator']) ? $this->configuration['operator'] : '>';
+    $form['operator'] = [
+      '#type' => 'select',
+      '#title' => t('Operator'),
+      '#options' => [
+        '==' => '=',
+        '>=' => '>=',
+        '>' => '>',
+        '<=' => '<=',
+        '<' => '<',
+      ],
+      '#default_value' => $operator,
+      '#required' => TRUE,
+    ];
+
     $form['amount'] = [
       '#type' => 'commerce_price',
       '#title' => t('Amount'),
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductEqual.php b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductEqual.php
new file mode 100644
index 0000000..19f2353
--- /dev/null
+++ b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductEqual.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionCondition;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\commerce_price\Price;
+
+/**
+ * Provides an 'Order: Total amount comparison' condition.
+ *
+ * @CommercePromotionCondition(
+ *   id = "commerce_promotion_product_equal",
+ *   label = @Translation("Product is"),
+ *   target_entity_type = "commerce_order_item",
+ * )
+ */
+class ProductEqual extends PromotionConditionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'entity' => NULL,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form += parent::buildConfigurationForm($form, $form_state);
+
+    $entity_id = isset($this->configuration['entity']) ? $this->configuration['entity'] : NULL;
+
+    $form['entity'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => t('Product'),
+      '#default_value' =>  \Drupal::entityTypeManager()->getStorage('commerce_product')->load($entity_id),
+      '#target_type' => 'commerce_product',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function evaluate() {
+    $entity_id = $this->configuration['entity'];
+    if (empty($entity_id)) {
+      return FALSE;
+    }
+    $entity = \Drupal::entityTypeManager()->getStorage('commerce_product')->load($entity_id);
+
+    /** @var \Drupal\commerce_product\Entity\ProductInterface $current_product */
+    $current_product = $this->getTargetEntity()->getPurchasedEntity()->getProduct();
+
+    return $current_product->id() == $entity->id() ? TRUE : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function summary() {
+    return $this->t('Compares the product entity.');
+  }
+
+}
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductFieldEqual.php b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductFieldEqual.php
new file mode 100644
index 0000000..9edad40
--- /dev/null
+++ b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductFieldEqual.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionCondition;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\commerce_price\Price;
+
+/**
+ * Provides an 'Order: Total amount comparison' condition.
+ *
+ * @CommercePromotionCondition(
+ *   id = "commerce_promotion_product_field_equal",
+ *   label = @Translation("Product field is"),
+ *   target_entity_type = "commerce_order_item",
+ * )
+ */
+class ProductEqual extends PromotionConditionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'entity' => NULL,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form += parent::buildConfigurationForm($form, $form_state);
+
+    $entity_id = isset($this->configuration['entity']) ? $this->configuration['entity'] : NULL;
+
+    $form['entity'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => t('Product'),
+      '#default_value' =>  \Drupal::entityTypeManager()->getStorage('commerce_product')->load($entity_id),
+      '#target_type' => 'commerce_product',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function evaluate() {
+    $entity_id = $this->configuration['entity'];
+    if (empty($entity_id)) {
+      return FALSE;
+    }
+    $entity = \Drupal::entityTypeManager()->getStorage('commerce_product')->load($entity_id);
+
+    /** @var \Drupal\commerce_product\Entity\ProductInterface $current_product */
+    $current_product = $this->getTargetEntity()->getPurchasedEntity()->getProduct();
+
+    $current_product->id() == $entity->id() ? TRUE : FALSE;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function summary() {
+    return $this->t('Compares the product entity.');
+  }
+
+}
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductVariationEqual.php b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductVariationEqual.php
new file mode 100644
index 0000000..8a5c75f
--- /dev/null
+++ b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductVariationEqual.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionCondition;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\commerce_price\Price;
+
+/**
+ * Provides an 'Order: Total amount comparison' condition.
+ *
+ * @CommercePromotionCondition(
+ *   id = "commerce_promotion_product_variation_equal",
+ *   label = @Translation("Product Variation is"),
+ *   target_entity_type = "commerce_order_item",
+ * )
+ */
+class ProductVariationEqual extends PromotionConditionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'entity' => NULL,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form += parent::buildConfigurationForm($form, $form_state);
+
+    $entity_id = isset($this->configuration['entity']) ? $this->configuration['entity'] : NULL;
+
+    $form['entity'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => t('Product variation'),
+      '#default_value' =>  \Drupal::entityTypeManager()->getStorage('commerce_product_variation')->load($entity_id),
+      '#target_type' => 'commerce_product_variation',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function evaluate() {
+    $entity_id = $this->configuration['entity'];
+    if (empty($entity_id)) {
+      return FALSE;
+    }
+    $entity = \Drupal::entityTypeManager()->getStorage('commerce_product_variation')->load($entity_id);
+
+    /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $current_product_variation */
+    $current_product_variation = $this->getTargetEntity()->getPurchasedEntity();
+
+    return $current_product_variation->id() == $entity->id() ? TRUE : FALSE;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function summary() {
+    return $this->t('Compares the product variation entity.');
+  }
+
+}
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductVariationFieldEqual.php b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductVariationFieldEqual.php
new file mode 100644
index 0000000..3164429
--- /dev/null
+++ b/modules/promotion/src/Plugin/Commerce/PromotionCondition/ProductVariationFieldEqual.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionCondition;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\commerce_price\Price;
+
+/**
+ * Provides an 'Order: Total amount comparison' condition.
+ *
+ * @CommercePromotionCondition(
+ *   id = "commerce_promotion_product_variation_field_equal",
+ *   label = @Translation("Product Variation field is"),
+ *   target_entity_type = "commerce_order_item",
+ * )
+ */
+class ProductVariationEqual extends PromotionConditionBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'entity' => NULL,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form += parent::buildConfigurationForm($form, $form_state);
+
+    $entity_id = isset($this->configuration['entity']) ? $this->configuration['entity'] : NULL;
+
+    $form['entity'] = [
+      '#type' => 'entity_autocomplete',
+      '#title' => t('Product variation'),
+      '#default_value' =>  \Drupal::entityTypeManager()->getStorage('commerce_product_variation')->load($entity_id),
+      '#target_type' => 'commerce_product_variation',
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function evaluate() {
+    $entity_id = $this->configuration['entity'];
+    if (empty($entity_id)) {
+      return FALSE;
+    }
+    $entity = \Drupal::entityTypeManager()->getStorage('commerce_product_variation')->load($entity_id);
+
+    /** @var \Drupal\commerce_product\Entity\ProductVariationInterface $current_product_variation */
+    $current_product_variation = $this->getTargetEntity()->getPurchasedEntity();
+
+    $current_product_variation->id() == $entity->id() ? TRUE : FALSE;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function summary() {
+    return $this->t('Compares the product variation entity.');
+  }
+
+}
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionOffer/FixedOffBase.php b/modules/promotion/src/Plugin/Commerce/PromotionOffer/FixedOffBase.php
new file mode 100644
index 0000000..8ea6bc1
--- /dev/null
+++ b/modules/promotion/src/Plugin/Commerce/PromotionOffer/FixedOffBase.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionOffer;
+
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Provides the base class for percentage off offers.
+ */
+abstract class FixedOffBase extends PromotionOfferBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'amount' => 0,
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * Gets the percentage amount, as a decimal, negated.
+   *
+   * @return string
+   *   The amount.
+   */
+  public function getAmount() {
+    return (string) $this->configuration['amount'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form += parent::buildConfigurationForm($form, $form_state);
+
+    $form['amount'] = [
+      '#type' => 'commerce_number',
+      '#title' => $this->t('Amount'),
+      '#default_value' => $this->configuration['amount'],
+      '#maxlength' => 255,
+      '#required' => TRUE,
+      '#min' => 0,
+      '#size' => 8,
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
+    $values = $form_state->getValue($form['#parents']);
+    if (empty($values['target_plugin_configuration']['amount'])) {
+      $form_state->setError($form, $this->t('Fixed amount cannot be empty.'));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    $values = $form_state->getValue($form['#parents']);
+    $this->configuration['amount'] = $values['amount'];
+    parent::submitConfigurationForm($form, $form_state);
+  }
+
+}
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderFixedOff.php b/modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderFixedOff.php
new file mode 100644
index 0000000..0591f4e
--- /dev/null
+++ b/modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderFixedOff.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionOffer;
+
+/**
+ * Provides a 'Order: Fixed off' condition.
+ *
+ * @CommercePromotionOffer(
+ *   id = "commerce_promotion_order_fixed_off",
+ *   label = @Translation("Fixed off"),
+ *   target_entity_type = "commerce_order"
+ * )
+ */
+class OrderFixedOff extends FixedOffBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute() {
+    /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
+    $order = $this->getTargetEntity();
+    $adjustment_amount = $order->getTotalPrice()->multiply($this->getAmount());
+    $adjustment_amount = $this->rounder->round($adjustment_amount);
+    $this->applyAdjustment($order, $adjustment_amount);
+  }
+
+}
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderPercentageOff.php b/modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderPercentageOff.php
index 9b44836..8dd6d25 100644
--- a/modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderPercentageOff.php
+++ b/modules/promotion/src/Plugin/Commerce/PromotionOffer/OrderPercentageOff.php
@@ -8,7 +8,7 @@ namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionOffer;
  * @CommercePromotionOffer(
  *   id = "commerce_promotion_order_percentage_off",
  *   label = @Translation("Percentage off"),
- *   target_entity_type = "commerce_order",
+ *   target_entity_type = "commerce_order"
  * )
  */
 class OrderPercentageOff extends PercentageOffBase {
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionOffer/PercentageOffBase.php b/modules/promotion/src/Plugin/Commerce/PromotionOffer/PercentageOffBase.php
index 2ae1a21..42a2da7 100644
--- a/modules/promotion/src/Plugin/Commerce/PromotionOffer/PercentageOffBase.php
+++ b/modules/promotion/src/Plugin/Commerce/PromotionOffer/PercentageOffBase.php
@@ -54,7 +54,7 @@ abstract class PercentageOffBase extends PromotionOfferBase {
    */
   public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
     $values = $form_state->getValue($form['#parents']);
-    if (empty($values['amount'])) {
+    if (empty($values['target_plugin_configuration']['amount'])) {
       $form_state->setError($form, $this->t('Percentage amount cannot be empty.'));
     }
   }
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionOffer/ProductFixedOff.php b/modules/promotion/src/Plugin/Commerce/PromotionOffer/ProductFixedOff.php
new file mode 100644
index 0000000..1142dd2
--- /dev/null
+++ b/modules/promotion/src/Plugin/Commerce/PromotionOffer/ProductFixedOff.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionOffer;
+use Drupal\commerce_price\Price;
+
+/**
+ * Provides a 'Product: Fixed off' condition.
+ *
+ * @CommercePromotionOffer(
+ *   id = "commerce_promotion_product_fixed_off",
+ *   label = @Translation("Fixed off"),
+ *   target_entity_type = "commerce_order_item"
+ * )
+ */
+class ProductFixedOff extends FixedOffBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function execute() {
+    /** @var \Drupal\commerce_order\Entity\OrderItemInterface $order_item */
+    $order_item = $this->getTargetEntity();
+    $adjustment_amount = new Price($this->getAmount(), $order_item->getUnitPrice()->getCurrencyCode());
+    $adjustment_amount = $this->rounder->round($adjustment_amount);
+    $this->applyAdjustment($order_item, $adjustment_amount);
+  }
+
+}
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionOffer/ProductPercentageOff.php b/modules/promotion/src/Plugin/Commerce/PromotionOffer/ProductPercentageOff.php
index cffe155..6263000 100644
--- a/modules/promotion/src/Plugin/Commerce/PromotionOffer/ProductPercentageOff.php
+++ b/modules/promotion/src/Plugin/Commerce/PromotionOffer/ProductPercentageOff.php
@@ -8,7 +8,7 @@ namespace Drupal\commerce_promotion\Plugin\Commerce\PromotionOffer;
  * @CommercePromotionOffer(
  *   id = "commerce_promotion_product_percentage_off",
  *   label = @Translation("Percentage off"),
- *   target_entity_type = "commerce_order_item",
+ *   target_entity_type = "commerce_order_item"
  * )
  */
 class ProductPercentageOff extends PercentageOffBase {
diff --git a/modules/promotion/src/Plugin/Commerce/PromotionOffer/PromotionOfferBase.php b/modules/promotion/src/Plugin/Commerce/PromotionOffer/PromotionOfferBase.php
index 1d866e3..4f4e89b 100644
--- a/modules/promotion/src/Plugin/Commerce/PromotionOffer/PromotionOfferBase.php
+++ b/modules/promotion/src/Plugin/Commerce/PromotionOffer/PromotionOfferBase.php
@@ -6,6 +6,7 @@ use Drupal\commerce_order\EntityAdjustableInterface;
 use Drupal\commerce_order\Adjustment;
 use Drupal\commerce_price\Price;
 use Drupal\commerce_price\RounderInterface;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Executable\ExecutablePluginBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Plugin\ContextAwarePluginAssignmentTrait;
@@ -74,12 +75,19 @@ abstract class PromotionOfferBase extends ExecutablePluginBase implements Promot
    * {@inheritdoc}
    */
   public function applyAdjustment(EntityAdjustableInterface $entity, Price $amount) {
+    $source = $this->getContextValue('source');
+    if (!$source || !$source instanceof EntityInterface) {
+      $source_id = $this->getPluginId();
+    }
+    else {
+      $source_id = implode(':', [$source->getEntityTypeId(), $source->id()]);
+    }
     $entity->addAdjustment(new Adjustment([
       'type' => 'promotion',
       // @todo Change to label from UI when added in #2770731.
-      'label' => t('Discount'),
+      'label' => $this->getTitle(),
       'amount' => $amount->multiply('-1'),
-      'source_id' => $this->getPluginId(),
+      'source_id' => $source_id,
     ]));
   }
 
@@ -89,6 +97,15 @@ abstract class PromotionOfferBase extends ExecutablePluginBase implements Promot
   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
     $contexts = $form_state->getTemporaryValue('gathered_contexts') ?: [];
     $form['context_mapping'] = $this->addContextAssignmentElement($this, $contexts);
+
+    $form['title'] = [
+      '#type' => 'textfield',
+      '#title' => $this->t('Human readable offer name'),
+      '#default_value' => $this->configuration['title'],
+      '#maxlength' => 255,
+      '#required' => TRUE,
+    ];
+
     return $form;
   }
 
@@ -113,7 +130,19 @@ abstract class PromotionOfferBase extends ExecutablePluginBase implements Promot
    * {@inheritdoc}
    */
   public function defaultConfiguration() {
-    return [];
+    return [
+      'title' => '',
+    ];
+  }
+
+  /**
+   * Gets the percentage amount, as a decimal, negated.
+   *
+   * @return string
+   *   The amount.
+   */
+  public function getTitle() {
+    return (string) $this->configuration['title'];
   }
 
   /**
