diff --git a/modules/salesforce_mapping/src/Entity/MappedObject.php b/modules/salesforce_mapping/src/Entity/MappedObject.php
index b5862b6..78b707b 100644
--- a/modules/salesforce_mapping/src/Entity/MappedObject.php
+++ b/modules/salesforce_mapping/src/Entity/MappedObject.php
@@ -483,9 +483,8 @@ class MappedObject extends RevisionableContentEntityBase implements MappedObject
     // @TODO better way to handle push/pull:
     $fields = $mapping->getPullFields();
     foreach ($fields as $field) {
-      // @TODO: The field plugin should be in charge of setting its value on an entity, we should not assume the field plugin's logic as we're doing here.
       try {
-        $value = $this->sf_object->field($field->get('salesforce_field'));
+        $value = $field->pullValue($this->sf_object, $this->drupal_entity, $mapping);
       }
       catch (\Exception $e) {
         // Field missing from SObject? Skip it.
diff --git a/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedIDs.php b/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedIDs.php
index 3324fce..6ea9058 100644
--- a/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedIDs.php
+++ b/modules/salesforce_mapping/src/Plugin/SalesforceMappingField/RelatedIDs.php
@@ -5,6 +5,9 @@ namespace Drupal\salesforce_mapping\Plugin\SalesforceMappingField;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\field\Entity\FieldConfig;
+use Drupal\salesforce\Exception as SalesforceException;
+use Drupal\salesforce\SFID;
+use Drupal\salesforce\SObject;
 use Drupal\salesforce_mapping\SalesforceMappingFieldPluginBase;
 use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface;
 
@@ -77,6 +80,33 @@ class RelatedIDs extends SalesforceMappingFieldPluginBase {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function pullValue(SObject $sf_object, EntityInterface $entity, SalesforceMappingInterface $mapping) {
+
+    if (!$this->pull() || empty($this->config('salesforce_field'))) {
+      throw new SalesforceException('No data to pull. Salesforce field mapping is not defined.');
+    }
+
+    $value = $sf_object->field($this->config('salesforce_field'));
+
+    // If value is not an SFID, make it one.
+    if (!($value instanceof SFID)) {
+      $value = new SFID($value);
+    }
+
+    // Convert SF Id to Drupal Id.
+    $referenced_mappings = $this->mapped_object_storage->loadBySfid($value);
+    if (!empty($referenced_mappings)) {
+      $mapped_object = reset($referenced_mappings);
+      return $mapped_object->getMappedEntity()->id();
+    }
+    else {
+      throw new SalesforceException('No Drupal entity mapped to the Salesforce Id.');
+    }
+  }
+
+  /**
    *
    */
   private function getConfigurationOptions($mapping) {
diff --git a/modules/salesforce_mapping/src/SalesforceMappingFieldPluginBase.php b/modules/salesforce_mapping/src/SalesforceMappingFieldPluginBase.php
index 4e9cc5e..e48b776 100644
--- a/modules/salesforce_mapping/src/SalesforceMappingFieldPluginBase.php
+++ b/modules/salesforce_mapping/src/SalesforceMappingFieldPluginBase.php
@@ -20,15 +20,17 @@ use Drupal\Core\Plugin\PluginBase;
 use Drupal\Core\Plugin\PluginFormInterface;
 use Drupal\salesforce\Event\SalesforceEvents;
 use Drupal\salesforce\Event\SalesforceWarningEvent;
+use Drupal\salesforce\Exception as SalesforceException;
 use Drupal\salesforce\Rest\RestClientInterface;
 use Drupal\salesforce\SFID;
+use Drupal\salesforce\SObject;
 use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface;
 use Drupal\salesforce_mapping\MappedObjectStorage;
 use Drupal\salesforce_mapping\SalesforceMappingFieldPluginInterface;
 use Drupal\salesforce_mapping\SalesforceMappingStorage;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
-  
+
 /**
  * Defines a base Salesforce Mapping Field Plugin implementation.
  * Extenders need to implement SalesforceMappingFieldPluginInterface::value() and
@@ -99,8 +101,8 @@ abstract class SalesforceMappingFieldPluginBase extends PluginBase implements Sa
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
-    return new static($configuration, $plugin_id, $plugin_definition, 
-      $container->get('entity_type.bundle.info'),   
+    return new static($configuration, $plugin_id, $plugin_definition,
+      $container->get('entity_type.bundle.info'),
       $container->get('entity_field.manager'),
       $container->get('salesforce.client'),
       $container->get('entity.manager'),
@@ -130,7 +132,7 @@ abstract class SalesforceMappingFieldPluginBase extends PluginBase implements Sa
       return $value;
     }
 
-    // objectDescribe can throw an exception, but that's outside the scope of 
+    // objectDescribe can throw an exception, but that's outside the scope of
     // being handled here. Allow it to percolate.
     $describe = $this
       ->salesforceClient
@@ -208,6 +210,104 @@ abstract class SalesforceMappingFieldPluginBase extends PluginBase implements Sa
   /**
    * {@inheritdoc}
    */
+  public function pullValue(SObject $sf_object, EntityInterface $entity, SalesforceMappingInterface $mapping) {
+    // @TODO to provide for better extensibility, this would be better implemented as some kind of constraint or plugin system. That would also open new possibilities for injecting business logic into he mapping layer.
+
+    if (!$this->pull() || empty($this->config('salesforce_field'))) {
+      throw new SalesforceException('No data to pull. Salesforce field mapping is not defined.');
+    }
+
+    $value = $sf_object->field($this->config('salesforce_field'));
+
+    // objectDescribe can throw an exception, but that's outside the scope of
+    // being handled here. Allow it to percolate.
+    $describe = $this
+      ->salesforceClient
+      ->objectDescribe($mapping->getSalesforceObjectType());
+
+    try {
+      $field_definition = $describe->getField($this->config('salesforce_field'));
+    }
+    catch (\Exception $e) {
+      $this->eventDispatcher->dispatch(SalesforceEvents::WARNING, new SalesforceWarningEvent($e, 'Field definition not found for %describe.%field', ['%describe' => $describe->getName(), '%field' => $this->config('salesforce_field')]));
+
+      throw new SalesforceException(
+        sprintf('Field definition not found for %s.%s', $describe->getName(), $this->config('salesforce_field'))
+      );
+    }
+
+    switch (strtolower($field_definition['type'])) {
+      case 'boolean':
+        if (is_string($value) && strtolower($value) === 'false') {
+          $value = FALSE;
+        }
+        $value = (bool) $value;
+        break;
+
+      case 'date':
+        $tmp = $value;
+        if (!is_int($tmp)) {
+          $tmp = strtotime($tmp);
+        }
+        if (!empty($tmp)) {
+          $value = $this->dateFormatter->format($tmp, 'custom', DATETIME_DATE_STORAGE_FORMAT, 'UTC');
+        }
+        break;
+
+      case 'datetime':
+        $tmp = $value;
+        if (!is_int($tmp)) {
+          $tmp = strtotime($tmp);
+        }
+        if (!empty($tmp)) {
+          $value = $this->dateFormatter->format($tmp, 'custom', DATETIME_DATETIME_STORAGE_FORMAT, 'UTC');
+        }
+        break;
+
+      case 'double':
+        $value = (double) $value;
+        break;
+
+      case 'integer':
+        $value = (int) $value;
+        break;
+
+      case 'multipicklist':
+        if (!is_array($value)) {
+          $value = explode(';', $value);
+          $value = array_map('trim', $value);
+        }
+        break;
+
+      case 'id':
+      case 'reference':
+        if (empty($value)) {
+          break;
+        }
+        // If value is an SFID, cast to string.
+        if ($value instanceof SFID) {
+          $value = (string) $value;
+        }
+        // Otherwise, send it through SFID constructor & cast to validate.
+        else {
+          $value = (string) (new SFID($value));
+        }
+        break;
+
+      default:
+        if (is_string($value)) {
+          $value = substr($value, 0, $field_definition['length']);
+        }
+        break;
+
+    }
+
+    return $value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getConfiguration() {
     return $this->configuration;
   }
@@ -292,14 +392,14 @@ abstract class SalesforceMappingFieldPluginBase extends PluginBase implements Sa
    * Implements PluginFormInterface::validateConfigurationForm().
    */
   public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
-    
+
   }
 
   /**
    * Implements PluginFormInterface::submitConfigurationForm().
    */
   public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
-    
+
   }
 
   /**
@@ -327,7 +427,7 @@ abstract class SalesforceMappingFieldPluginBase extends PluginBase implements Sa
    * @see \Drupal\Core\Entity\EntityInterface::getConfigDependencyName()
    */
   public function calculateDependencies() {
-    
+
   }
 
   /**
@@ -358,7 +458,7 @@ abstract class SalesforceMappingFieldPluginBase extends PluginBase implements Sa
    */
   public function push() {
     return in_array($this->config('direction'), [
-      MappingConstants::SALESFORCE_MAPPING_DIRECTION_DRUPAL_SF, 
+      MappingConstants::SALESFORCE_MAPPING_DIRECTION_DRUPAL_SF,
       MappingConstants::SALESFORCE_MAPPING_DIRECTION_SYNC
     ]);
   }
@@ -370,7 +470,7 @@ abstract class SalesforceMappingFieldPluginBase extends PluginBase implements Sa
    */
   public function pull() {
     return in_array($this->config('direction'), [
-      MappingConstants::SALESFORCE_MAPPING_DIRECTION_SYNC, 
+      MappingConstants::SALESFORCE_MAPPING_DIRECTION_SYNC,
       MappingConstants::SALESFORCE_MAPPING_DIRECTION_SF_DRUPAL
     ]);
   }
diff --git a/modules/salesforce_mapping/src/SalesforceMappingFieldPluginInterface.php b/modules/salesforce_mapping/src/SalesforceMappingFieldPluginInterface.php
index 5344bea..b8d6360 100644
--- a/modules/salesforce_mapping/src/SalesforceMappingFieldPluginInterface.php
+++ b/modules/salesforce_mapping/src/SalesforceMappingFieldPluginInterface.php
@@ -3,6 +3,7 @@
 namespace Drupal\salesforce_mapping;
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\salesforce\SObject;
 use Drupal\salesforce_mapping\Entity\SalesforceMappingInterface;
 
 /**
@@ -54,13 +55,25 @@ interface SalesforceMappingFieldPluginInterface {
    * validation against Salesforce field types to protect against basic data
    * errors.
    *
-   * @param EntityInterface $entity 
-   * @param SalesforceMappingInterface $mapping 
+   * @param EntityInterface $entity
+   * @param SalesforceMappingInterface $mapping
    * @return mixed
    */
   public function pushValue(EntityInterface $entity, SalesforceMappingInterface $mapping);
 
   /**
+   * An extension of ::value, ::pullValue does some basic type-checking and
+   * validation against Drupal field types to protect against basic data
+   * errors.
+   *
+   * @param SObject $sf_object
+   * @param EntityInterface $entity
+   * @param SalesforceMappingInterface $mapping
+   * @return mixed
+   */
+  public function pullValue(SObject $sf_object, EntityInterface $entity, SalesforceMappingInterface $mapping);
+
+  /**
    * Given a SF Mapping, return TRUE or FALSE whether this field plugin can be
    * added via UI. Not used for validation or any other constraints.
    *
@@ -75,8 +88,8 @@ interface SalesforceMappingFieldPluginInterface {
   /**
    * Get/set a key-value config pair for this plugin.
    *
-   * @param string $key 
-   * @param mixed $value 
+   * @param string $key
+   * @param mixed $value
    */
   public function config($key = NULL, $value = NULL);
 
