diff --git a/config/vufind/Alma.ini b/config/vufind/Alma.ini
index 3b2017ad18ab6a19d28d83b165b428ac8f6d0966..f5bda3647c7df1221eeef3f95d08075b6b4f1bea 100644
--- a/config/vufind/Alma.ini
+++ b/config/vufind/Alma.ini
@@ -87,3 +87,10 @@ purgeDate =
 ; The webhook secret. This must be the same value that was added to the Alma webhook configuration as a secret.
 secret = YOUR_WEBHOOK_SECRET_FROM_ALMA
 
+[Holdings]
+; The digital delivery URL for your Alma instance. Replace at least SOMETHING and
+; INSTITUTION with correct values.
+;digitalDeliveryUrl = "https://SOMETHING.alma.exlibrisgroup.com/view/delivery/INSTITUTION/%%id%%"
+; Inventory types to display from Alma. A colon-separated list. Supported values
+; are "physical", "electronic" and "digital". By default all are displayed.
+;inventoryTypes = "physical:electronic"
diff --git a/module/VuFind/src/VuFind/ILS/Connection.php b/module/VuFind/src/VuFind/ILS/Connection.php
index 6b598e6f7437dadb48f4ff36755a9e71a3eae57a..702baa849a89999871462695effcf07d3064832e 100644
--- a/module/VuFind/src/VuFind/ILS/Connection.php
+++ b/module/VuFind/src/VuFind/ILS/Connection.php
@@ -1016,6 +1016,7 @@ class Connection implements TranslatorAwareInterface, LoggerAwareInterface
         return [
             'total' => $holdings['total'] ?? count($holdings),
             'holdings' => $holdings['holdings'] ?? $holdings,
+            'electronic_holdings' => $holdings['electronic_holdings'] ?? [],
             'page' => $finalOptions['page'],
             'itemLimit' => $finalOptions['itemLimit'],
         ];
diff --git a/module/VuFind/src/VuFind/ILS/Driver/Alma.php b/module/VuFind/src/VuFind/ILS/Driver/Alma.php
index 82791112b48cafcc6ece66e1adbd30b5475b4f1e..6c7ce16898907a95aab31c81d269ee56e1e972af 100644
--- a/module/VuFind/src/VuFind/ILS/Driver/Alma.php
+++ b/module/VuFind/src/VuFind/ILS/Driver/Alma.php
@@ -387,6 +387,24 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface
             }
         }
 
+        // Fetch also digital and/or electronic inventory if configured
+        $types = $this->getInventoryTypes();
+        if (in_array('d_avail', $types) || in_array('e_avail', $types)) {
+            // No need for physical items
+            $key = array_search('p_avail', $types);
+            if (false !== $key) {
+                unset($types[$key]);
+            }
+            $statuses = $this->getStatusesForInventoryTypes((array)$id, $types);
+            $electronic = [];
+            foreach ($statuses as $record) {
+                foreach ($record as $status) {
+                    $electronic[] = $status;
+                }
+            }
+            $results['electronic_holdings'] = $electronic;
+        }
+
         return $results;
     }
 
@@ -1202,60 +1220,7 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface
      */
     public function getStatuses($ids)
     {
-        $results = [];
-        $params = [
-            'mms_id' => implode(',', $ids),
-            'expand' => 'p_avail,e_avail,d_avail'
-        ];
-        if ($bibs = $this->makeRequest('/bibs', $params)) {
-            foreach ($bibs as $bib) {
-                $marc = new \File_MARCXML(
-                    $bib->record->asXML(),
-                    \File_MARCXML::SOURCE_STRING
-                );
-                $status = [];
-                $tmpl = [
-                    'id' => (string)$bib->mms_id,
-                    'source' => 'Solr',
-                    'callnumber' => isset($bib->isbn)
-                        ? (string)$bib->isbn
-                        : ''
-                ];
-                if ($record = $marc->next()) {
-                    // Physical
-                    $physicalItems = $record->getFields('AVA');
-                    foreach ($physicalItems as $field) {
-                        $avail = $field->getSubfield('e')->getData();
-                        $item = $tmpl;
-                        $item['availability'] = strtolower($avail) === 'available';
-                        $item['location'] = (string)$field->getSubfield('c')
-                            ->getData();
-                        $status[] = $item;
-                    }
-                    // Electronic
-                    $electronicItems = $record->getFields('AVE');
-                    foreach ($electronicItems as $field) {
-                        $avail = $field->getSubfield('e')->getData();
-                        $item = $tmpl;
-                        $item['availability'] = strtolower($avail) === 'available';
-                        $status[] = $item;
-                    }
-                    // Digital
-                    $digitalItems = $record->getFields('AVD');
-                    foreach ($digitalItems as $field) {
-                        $avail = $field->getSubfield('e')->getData();
-                        $item = $tmpl;
-                        $item['availability'] = strtolower($avail) === 'available';
-                        $status[] = $item;
-                    }
-                } else {
-                    // TODO: Throw error
-                    error_log('no record');
-                }
-                $results[] = $status;
-            }
-        }
-        return $results;
+        return $this->getStatusesForInventoryTypes($ids, $this->getInventoryTypes());
     }
 
     /**
@@ -1486,8 +1451,9 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface
         $xml = $this->makeRequest('/courses/' . $courseID . '/reading-lists');
         $reserves = [];
         foreach ($xml as $list) {
+            $listId = $list->id;
             $listXML = $this->makeRequest(
-                "/courses/${$courseID}/reading-lists/${$list->id}/citations"
+                "/courses/${$courseID}/reading-lists/${$listId}/citations"
             );
             foreach ($listXML as $citation) {
                 $reserves[$citation->id] = $citation->metadata;
@@ -1549,6 +1515,129 @@ class Alma extends AbstractBase implements \VuFindHttp\HttpServiceAwareInterface
         }
     }
 
+    /**
+     * Get the inventory types to be displayed. Possible values are:
+     * p_avail,e_avail,d_avail
+     *
+     * @return array
+     */
+    protected function getInventoryTypes()
+    {
+        $types = explode(
+            ':',
+            $this->config['Holdings']['inventoryTypes']
+                ?? 'physical:digital:electronic'
+        );
+
+        $result = [];
+        $map = [
+            'physical' => 'p_avail',
+            'digital' => 'd_avail',
+            'electronic' => 'e_avail'
+        ];
+        $types = array_flip($types);
+        foreach ($map as $src => $dest) {
+            if (isset($types[$src])) {
+                $result[] = $dest;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get Statuses for inventory types
+     *
+     * This is responsible for retrieving the status information for a
+     * collection of records with specified inventory types.
+     *
+     * @param array $ids   The array of record ids to retrieve the status for
+     * @param array $types Inventory types
+     *
+     * @return array An array of getStatus() return values on success.
+     */
+    protected function getStatusesForInventoryTypes($ids, $types)
+    {
+        $results = [];
+        $params = [
+            'mms_id' => implode(',', $ids),
+            'expand' => implode(',', $types)
+        ];
+        if ($bibs = $this->makeRequest('/bibs', $params)) {
+            foreach ($bibs as $bib) {
+                $marc = new \File_MARCXML(
+                    $bib->record->asXML(),
+                    \File_MARCXML::SOURCE_STRING
+                );
+                $status = [];
+                $tmpl = [
+                    'id' => (string)$bib->mms_id,
+                    'source' => 'Solr',
+                    'callnumber' => isset($bib->isbn)
+                        ? (string)$bib->isbn
+                        : ''
+                ];
+                if ($record = $marc->next()) {
+                    // Physical
+                    $physicalItems = $record->getFields('AVA');
+                    foreach ($physicalItems as $field) {
+                        $avail = $field->getSubfield('e')->getData();
+                        $item = $tmpl;
+                        $item['availability'] = strtolower($avail) === 'available';
+                        $item['location'] = (string)$field->getSubfield('c')
+                            ->getData();
+                        $status[] = $item;
+                    }
+                    // Electronic
+                    $electronicItems = $record->getFields('AVE');
+                    foreach ($electronicItems as $field) {
+                        $avail = $field->getSubfield('e')->getData();
+                        $item = $tmpl;
+                        $item['availability'] = strtolower($avail) === 'available';
+                        $item['location'] = $field->getSubfield('m')->getData();
+                        $item['location'] = $field->getSubfield('m')->getData();
+                        $url = $field->getSubfield('u')->getData();
+                        if (preg_match('/^https?:\/\//', $url)) {
+                            $item['locationhref'] = $url;
+                        }
+                        $item['status'] = $field->getSubfield('s')->getData();
+                        $status[] = $item;
+                    }
+                    // Digital
+                    $deliveryUrl
+                        = $this->config['Holdings']['digitalDeliveryUrl'] ?? '';
+                    $digitalItems = $record->getFields('AVD');
+                    if ($digitalItems && !$deliveryUrl) {
+                        $this->logWarning(
+                            'Digital items exist for ' . (string)$bib->mms_id
+                            . ', but digitalDeliveryUrl not set -- unable to'
+                            . ' generate links'
+                        );
+                    }
+                    foreach ($digitalItems as $field) {
+                        $item = $tmpl;
+                        unset($item['callnumber']);
+                        $item['availability'] = true;
+                        $item['location'] = $field->getSubfield('e')->getData();
+                        if ($deliveryUrl) {
+                            $item['locationhref'] = str_replace(
+                                '%%id%%',
+                                $field->getSubfield('b')->getData(),
+                                $deliveryUrl
+                            );
+                        }
+                        $status[] = $item;
+                    }
+                } else {
+                    // TODO: Throw error
+                    error_log('no record');
+                }
+                $results[(string)$bib->mms_id] = $status;
+            }
+        }
+        return $results;
+    }
+
     // @codingStandardsIgnoreStart
 
     /**
diff --git a/module/VuFind/src/VuFind/ILS/Driver/Demo.php b/module/VuFind/src/VuFind/ILS/Driver/Demo.php
index 0aede2d63aea41ba5ca36514e47861726e7bb660..af9f7b18b5da2bf2a57d097100ec71891f60f596 100644
--- a/module/VuFind/src/VuFind/ILS/Driver/Demo.php
+++ b/module/VuFind/src/VuFind/ILS/Driver/Demo.php
@@ -376,7 +376,7 @@ class Demo extends AbstractBase
         $status = $this->getFakeStatus();
         $location = $this->getFakeLoc();
         $locationhref = ($location === 'Campus A') ? 'http://campus-a' : false;
-        return [
+        $result = [
             'id'           => $id,
             'source'       => $this->getRecordSource(),
             'item_id'      => $number,
@@ -396,8 +396,26 @@ class Demo extends AbstractBase
             'addStorageRetrievalRequestLink' => $patron ? 'check' : false,
             'ILLRequest'   => 'auto',
             'addILLRequestLink' => $patron ? 'check' : false,
-            'services'     => $status == 'Available' ? $this->getFakeServices() : []
+            'services'     => $status == 'Available' ? $this->getFakeServices() : [],
         ];
+
+        switch (rand(1, 5)) {
+        case 1:
+            $result['location'] = 'Digital copy available';
+            $result['locationhref'] = 'http://digital';
+            $result['__electronic__'] = true;
+            $result['availability'] = true;
+            $result['status'] = '';
+            break;
+        case 2:
+            $result['location'] = 'Electronic Journals';
+            $result['locationhref'] = 'http://electronic';
+            $result['__electronic__'] = true;
+            $result['availability'] = true;
+            $result['status'] = 'Available from ' . rand(2010, 2019);
+        }
+
+        return $result;
     }
 
     /**
@@ -687,6 +705,14 @@ class Demo extends AbstractBase
             $status[$i]['enumchron'] = "volume $volume, issue $seriesIssue";
         }
 
+        // Filter out electronic holdings from the normal holdings list:
+        $status = array_filter(
+            $status,
+            function ($a) {
+                return !($a['__electronic__'] ?? false);
+            }
+        );
+
         // Slice out a chunk if pagination is enabled.
         $slice = null;
         if ($options['itemLimit'] ?? null) {
@@ -702,10 +728,22 @@ class Demo extends AbstractBase
             );
         }
 
+        // Electronic holdings:
+        $statuses = $this->getStatus($id);
+        $electronic = [];
+        foreach ($statuses as $item) {
+            if ($item['__electronic__'] ?? false) {
+                // Don't expose internal __electronic__ flag upstream:
+                unset($item['__electronic__']);
+                $electronic[] = $item;
+            }
+        }
+
         // Send back final value:
         return [
             'total' => count($status),
             'holdings' => $slice ?: $status,
+            'electronic_holdings' => $electronic
         ];
     }
 
diff --git a/module/VuFind/src/VuFind/ILS/Logic/Holds.php b/module/VuFind/src/VuFind/ILS/Logic/Holds.php
index e511ca4ea4183a8011357c5e362870b2d496f951..54f4524a8adc61c9b4cca1529f25ce9e0d160a07 100644
--- a/module/VuFind/src/VuFind/ILS/Logic/Holds.php
+++ b/module/VuFind/src/VuFind/ILS/Logic/Holds.php
@@ -234,7 +234,8 @@ class Holds
             'total' => $result['total'],
             'page' => $result['page'],
             'itemLimit' => $result['itemLimit'],
-            'holdings' => $this->formatHoldings($holdings)
+            'holdings' => $this->formatHoldings($holdings),
+            'electronic_holdings' => $result['electronic_holdings'] ?? [],
         ];
     }
 
diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml
index 1fe34f0872ce5e4e7a24825e61a1bdcf4cfc4d72..b116e80731e73a742b065973237bcb85642a8497 100644
--- a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml
+++ b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml
@@ -12,7 +12,13 @@
     try {
         $holdings = $this->driver->getRealTimeHoldings();
     } catch (\VuFind\Exception\ILS $e) {
-        $holdings = ['holdings' => []];
+        $holdings = [
+          'holdings' => [],
+          'electronic_holdings' => [],
+          'total' => 0,
+          'page' => 0,
+          'itemLimit' => 0
+        ];
         $offlineMode = 'ils-offline';
     }
     // Set page title.
@@ -28,6 +34,7 @@
 <?php endif; ?>
 
 <?=($offlineMode == "ils-offline") ? $this->render('Helpers/ils-offline.phtml', ['offlineModeMsg' => 'ils_offline_holdings_message']) : ''?>
+
 <?php if (($this->ils()->getHoldsMode() == 'driver' && !empty($holdings['holdings'])) || $this->ils()->getTitleHoldsMode() == 'driver'): ?>
   <?php if ($account->loginEnabled() && $offlineMode != 'ils-offline'): ?>
     <?php if (!$user): ?>
@@ -54,6 +61,14 @@
   <?php if ($openUrlActive): ?><?=$openUrl->renderTemplate()?><?php endif; ?>
   <?php if ($doiActive): ?><?=$doi->renderTemplate()?><?php endif; ?>
 <?php endif; ?>
+
+<?php if (!empty($holdings['electronic_holdings'])): ?>
+  <?=$this->context($this)->renderInContext(
+      'RecordTab/holdingsils/electronic.phtml',
+      ['holdings' => $holdings['electronic_holdings']]
+  );?>
+<?php endif; ?>
+
 <?php foreach ($holdings['holdings'] ?? [] as $holding): ?>
 <h3>
   <?php $locationText = $this->transEsc('location_' . $holding['location'], [], $holding['location']); ?>
diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils/electronic.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils/electronic.phtml
new file mode 100644
index 0000000000000000000000000000000000000000..aba2d793985735d4ba0364a0245f3caabfe30e18
--- /dev/null
+++ b/themes/bootstrap3/templates/RecordTab/holdingsils/electronic.phtml
@@ -0,0 +1,24 @@
+<h3><?=$this->transEsc('Electronic')?></h3>
+<table class="table table-striped">
+  <?php foreach ($this->holdings as $item): ?>
+    <tr>
+      <td class="fullLocation">
+        <?php $locationText = $this->transEsc('location_' . $item['location'], [], $item['location']); ?>
+        <?php if ($item['locationhref'] ?? false): ?>
+          <a href="<?=$item['locationhref']?>" target="_blank"><?=$locationText?></a>
+        <?php else: ?>
+          <?=$locationText?>
+        <?php endif; ?>
+      </td>
+      <td class="fullAvailability">
+        <?php if ($item['use_unknown_message'] ?? false): ?>
+          <span><?=$this->transEsc('status_unknown_message')?></span>
+        <?php elseif ($item['availability']): ?>
+          <span class="text-success"><?=!empty($item['status']) ? $this->transEsc($item['status']) : $this->transEsc('Available')?></span>
+        <?php else: ?>
+          <span class="text-danger"><?=$this->transEsc($item['status'])?></span>
+        <?php endif; ?>
+      </td>
+    </tr>
+  <?php endforeach; ?>
+</table>