Skip to content

Commit

Permalink
DIS-34 Local ILL for holds
Browse files Browse the repository at this point in the history
- When placing a volume hold, display an error message if a volume has not been selected.
- When displaying actions for a record, if the record (or group of records) requires local ILL and all volumes are not part of the active hold group, show a message that requests are not available.
- When showing the list of volumes that a patron can place holds for in the modal, disable any volumes that cannot be requested.
  • Loading branch information
mdnoble73 committed Feb 10, 2025
1 parent acac368 commit f1f57a5
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 81 deletions.
116 changes: 76 additions & 40 deletions code/web/RecordDrivers/MarcRecordDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -1100,42 +1100,7 @@ public function getRecordActions($relatedRecord, $variationId, $isAvailable, $is
if ($volumeData == null) {
$volumeData = $relatedRecord->getVolumeData();
}
//See if we have InterLibrary Loan integration. If so, we will either be placing a hold or requesting depending on if there is a copy local to the hold group (whether available or not)
$interLibraryLoanType = 'none';
$treatHoldAsInterLibraryLoanRequest = false;
$homeLocation = null;
$holdGroups = [];
try {
$homeLocation = Location::getDefaultLocationForUser();
if ($homeLocation != null) {
$interLibraryLoanType = $homeLocation->getInterlibraryLoanType();
if ($interLibraryLoanType != 'none') {
$treatHoldAsInterLibraryLoanRequest = true;
require_once ROOT_DIR . '/sys/InterLibraryLoan/HoldGroup.php';
require_once ROOT_DIR . '/sys/InterLibraryLoan/HoldGroupLocation.php';

//Get the VDX Group(s) that we will interact with
$holdGroupsForLocation = new HoldGroupLocation();
$holdGroupsForLocation->locationId = $homeLocation->locationId;
$holdGroupIds = $holdGroupsForLocation->fetchAll('holdGroupId');
foreach ($holdGroupIds as $holdGroupId) {
$holdGroup = new HoldGroup();
$holdGroup->id = $holdGroupId;
if ($holdGroup->find(true)) {
$holdGroups[] = clone $holdGroup;
}
}

//Check to see if we have any items that are owned by any of the records in any of the groups.
//If we do, we don't need to use VDX
if ($this->oneOrMoreHoldableItemsOwnedByPatronHoldGroups($relatedRecord->getItems(), $holdGroups, $variationId, $homeLocation->code)) {
$treatHoldAsInterLibraryLoanRequest = false;
}
}
}
} catch (Exception $e) {
//This happens if the tables are not installed yet
}
list($interLibraryLoanType, $treatHoldAsInterLibraryLoanRequest, $homeLocation, $holdGroups) = $this->getInterLibraryLoanIntegrationInformation($relatedRecord, $variationId);

//Figure out what needs to happen with volumes (if anything)
$needsVolumeHold = false;
Expand Down Expand Up @@ -1175,13 +1140,34 @@ public function getRecordActions($relatedRecord, $variationId, $isAvailable, $is
}
}
}
$interface->assign('itemsWithoutVolumesNeedIllRequest', $itemsWithoutVolumesNeedIllRequest);

//Figure out the actions to add
if ($needsVolumeHold) {
//We can just show buttons for each volume if there are 3 or fewer volumes and no items without a volume
if (count($holdableVolumes) > 3 || count($itemsWithoutVolumes) > 0) {
//We will need to show a popup to select the volume
$interface->assign('itemsWithoutVolumesNeedIllRequest', $itemsWithoutVolumesNeedIllRequest);
$this->_actions[$variationId][] = getMultiVolumeHoldAction($this->getModule(), $source, $id);
//Check to see if all items require a request
$allVolumesRequireIll = true;
if ($interLibraryLoanType !== 'none') {
if (count($itemsWithoutVolumes) > 0 && !$itemsWithoutVolumesNeedIllRequest) {
$allVolumesRequireIll = false;
}
foreach ($holdableVolumes as $volumeInfo) {
if (!$volumeInfo['needsIllRequest']) {
$allVolumesRequireIll = false;
}
}
}else{
$allVolumesRequireIll = false;
}
if ($allVolumesRequireIll) {
//The button will show a message to the patron no volumes can be requested
$this->_actions[$variationId][] = getNoVolumesCanBeRequestedAction($this->getModule(), $source, $id);
}else{
//We will need to show a popup to select the volume
$interface->assign('itemsWithoutVolumesNeedIllRequest', $itemsWithoutVolumesNeedIllRequest);
$this->_actions[$variationId][] = getMultiVolumeHoldAction($this->getModule(), $source, $id);
}
}else{
//We show a button per volume
ksort($holdableVolumes);
Expand Down Expand Up @@ -1250,7 +1236,7 @@ public function getRecordActions($relatedRecord, $variationId, $isAvailable, $is
* @param string $patronHomeLocationCode - The location code for the patron's home location
* @return bool
*/
private function oneOrMoreHoldableItemsOwnedByPatronHoldGroups(array $items, array $holdGroups, int|string $variationId, string $patronHomeLocationCode) : bool {
public function oneOrMoreHoldableItemsOwnedByPatronHoldGroups(array $items, array $holdGroups, int|string $variationId, string $patronHomeLocationCode) : bool {
//If no hold groups exist, everything is valid
if (count($holdGroups) == 0) {
return true;
Expand Down Expand Up @@ -2963,6 +2949,56 @@ public function getViewable856Links(): array {
}
return $this->validUrls;
}

/**
* @param Grouping_Record|null $relatedRecord
* @param $variationId
* @return array
*/
public function getInterLibraryLoanIntegrationInformation(?Grouping_Record $relatedRecord, $variationId): array {
//See if we have InterLibrary Loan integration. If so, we will either be placing a hold or requesting depending on if there is a copy local to the hold group (whether available or not)
$interLibraryLoanType = 'none';
$treatHoldAsInterLibraryLoanRequest = false;
$homeLocation = null;
$holdGroups = [];
try {
$homeLocation = Location::getDefaultLocationForUser();
if ($homeLocation != null) {
$interLibraryLoanType = $homeLocation->getInterlibraryLoanType();
if ($interLibraryLoanType != 'none') {
$treatHoldAsInterLibraryLoanRequest = true;
require_once ROOT_DIR . '/sys/InterLibraryLoan/HoldGroup.php';
require_once ROOT_DIR . '/sys/InterLibraryLoan/HoldGroupLocation.php';

//Get the Hold Group(s) that we will interact with
$holdGroupsForLocation = new HoldGroupLocation();
$holdGroupsForLocation->locationId = $homeLocation->locationId;
$holdGroupIds = $holdGroupsForLocation->fetchAll('holdGroupId');
foreach ($holdGroupIds as $holdGroupId) {
$holdGroup = new HoldGroup();
$holdGroup->id = $holdGroupId;
if ($holdGroup->find(true)) {
$holdGroups[] = clone $holdGroup;
}
}

//Check to see if we have any items that are owned by any of the records in any of the groups.
//If we do, we don't need to use VDX
if ($this->oneOrMoreHoldableItemsOwnedByPatronHoldGroups($relatedRecord->getItems(), $holdGroups, $variationId, $homeLocation->code)) {
$treatHoldAsInterLibraryLoanRequest = false;
}
}
}
} catch (Exception $e) {
//This happens if the tables are not installed yet
}
return array(
$interLibraryLoanType,
$treatHoldAsInterLibraryLoanRequest,
$homeLocation,
$holdGroups
);
}
}


21 changes: 21 additions & 0 deletions code/web/RecordDrivers/RecordActionGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,27 @@ function getLocalIllRequestAction($module, $source, $id) : array {
];
}

function getNoVolumesCanBeRequestedAction($module, $source, $id) : array {
$title = translate([
'text' => 'Request Unavailable',
'isPublicFacing' => true,
'inAttribute' => true,
]);
$message = translate([
'text' => "Titles with volumes cannot be requested from other libraries via the catalog. Please contact the library to request this title.",
'isPublicFacing' => true,
'inAttribute' => true,
]);
return [
'title' => $title,
'url' => '',
'id' => "actionButton$id",
'onclick' => "AspenDiscovery.showMessage('$title', '$message');return false;",
'requireLogin' => false,
'type' => 'local_ill_request',
'btnType' => 'btn-local-ill-request btn-action'
];
}
//VDX Requests
function getVdxRequestAction($module, $source, $id) : array {
return [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
{rdelim});
{rdelim}
$('#userOption select').html(options);
{rdelim}).change(); /* trigger on initial load */
{rdelim}).change(); /* trigger on the initial load */
{rdelim});
</script>
{/if}
Expand All @@ -113,16 +113,11 @@
{/if}
<div id="volumeSelection" class="form-group" {if empty($majorityOfItemsHaveVolumes)}style="display: none" {/if}>
<select name="selectedVolume" id="selectedVolume" class="form-control" aria-label="{translate text="Selected Volume" isPublicFacing=true}">
<option value="">{translate text="Please select a volume from the list below" isPublicFacing=true}</option>
<option value="unselected" selected disabled>{translate text="Please select a volume from the list below" isPublicFacing=true}</option>
{foreach from=$volumes item=volume}
<option value="{$volume->volumeId}">{$volume->displayLabel} {if $alwaysPlaceVolumeHoldWhenVolumesArePresent && $volume->hasLocalItems()}({translate text="Owned by %1%" 1=$localSystemName isPublicFacing=true}){/if}</option>
<option value="{$volume->volumeId}" {if $volume->needsIllRequest()}disabled{/if}>{$volume->displayLabel} {if $alwaysPlaceVolumeHoldWhenVolumesArePresent && $volume->hasLocalItems()}({translate text="Owned by %1%" 1=$localSystemName isPublicFacing=true}){/if} {if $volume->needsIllRequest()}{translate text="Not Requestable" isPublicFacing=true}{/if}</option>
{/foreach}
</select>
{* {if !empty($alwaysPlaceVolumeHoldWhenVolumesArePresent)}*}
{* <span id="subdomainHelpBlock" class="help-block" style="margin-top:0">*}
{* <small class="text-warning"><i class="fas fa-exclamation-triangle"></i>{translate text="Volumes marked with a * have titles owned by this library." isPublicFacing=true}</small>*}
{* </span>*}
{* {/if}*}
</div>

{if $showHoldCancelDate == 1}
Expand Down Expand Up @@ -160,4 +155,4 @@
{translate text="Placing your hold, this may take a minute." isPublicFacing=true}
</div>
</div>
{/strip}
{/strip}
15 changes: 9 additions & 6 deletions code/web/interface/themes/responsive/js/aspen.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 9 additions & 6 deletions code/web/interface/themes/responsive/js/aspen/record.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,12 @@ AspenDiscovery.Record = (function () {
},

placeVolumeHold: function () {
var selectedVolume = $("#selectedVolume option:selected").val() ;
if (selectedVolume === 'unselected'){
alert("You must select a volume before continuing");
return false;
}

var requestTitleButton = $('#requestTitleButton');
requestTitleButton.prop('disabled', true);
requestTitleButton.addClass('disabled');
Expand All @@ -404,7 +410,7 @@ AspenDiscovery.Record = (function () {
var id = $('#id').val();
var autoLogOut = $('#autologout').prop('checked');
var module = $('#module').val();
var volume = $('#selectedVolume');

var params = {
'method': 'placeHold',
pickupBranch: $('#pickupBranch').val(),
Expand All @@ -417,11 +423,8 @@ AspenDiscovery.Record = (function () {
if (autoLogOut) {
params['autologout'] = true;
}
if (volume.length > 0) {
params['volume'] = volume.val();
} else {
alert("Please select a volume to place a hold on.");
return false;
if (selectedVolume.length > 0) {
params['volume'] = selectedVolume;
}
if (params['pickupBranch'] === 'undefined') {
alert("Please select a location to pick up your hold when it is ready.");
Expand Down
7 changes: 7 additions & 0 deletions code/web/release_notes/25.02.00.MD
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@
- When importing lists from Evergreen, ensure that list entries have their titles set so the lists can be sorted by title. (*MDN*)
- Correct error when processing PIN Reset within Evergreen. (*MDN*)

### Hold Updates
- When placing a volume hold, display an error message if a volume has not been selected. (DIS-34) (*MDN*)

### Indexing Profile Updates
- Add Indexing profiles in 2 steps. First set the name and indexing class and then set the remaining information. (DIS-230) (*MDN*)
- When creating an indexing profile, ensure that the name and record url component are unique and that the indexing profile is attached to an account profile. (DIS-230) (*MDN*)
Expand Down Expand Up @@ -163,6 +166,10 @@
- Patrons will be able to view and change their consent information (as retrieved from Koha) at any point through the ‘Your Account > Privacy Settings ’ section. (DIS-98) (*CZ*)
- If ILS consent is enabled in the Aspen settings, but the Koha plugin is disabled or uninstalled, patron will see an informative message in 'Privacy Settings'. (DIS-98) (*CZ*)

### Local ILL
- When displaying actions for a record, if the record (or group of records) requires local ILL and all volumes are not part of the active hold group, show a message that requests are not available. (DIS-34) (*MDN*)
- When showing the list of volumes that a patron can place holds for in the modal, disable any volumes that cannot be requested. (DIS-34) (*MDN*)

### Material Request Updates
- Allow some patron types to submit unlimited material requests. (DIS-256) (*MDN*)
- Allow requests per calendar year to be based on a start date selected by the library rather than using January 1st always. (DIS-291) (*MDN*)
Expand Down
40 changes: 23 additions & 17 deletions code/web/services/Record/AJAX.php
Original file line number Diff line number Diff line change
Expand Up @@ -758,20 +758,6 @@ function getPlaceHoldVolumesForm(): array {

$numItemsWithVolumes = 0;
$numItemsWithoutVolumes = 0;
/*foreach ($relatedRecord->getItems() as $item) {
if (!$item->isEContent){
if (empty($item->volume)) {
$numItemsWithoutVolumes++;
} else {
if ($item->libraryOwned || $item->locallyOwned) {
if (array_key_exists($item->volumeId, $volumeData)) {
$volumeData[$item->volumeId]->setHasLocalItems(true);
}
}
$numItemsWithVolumes++;
}
}
}*/
foreach ($relatedRecord->recordVariations as $variation) { // check variations for non-econtent items for records that have both econtent and physical items attached
if (!($variation->isEContent())) {
foreach ($variation->getRecords() as $record) {
Expand All @@ -780,8 +766,9 @@ function getPlaceHoldVolumesForm(): array {
if (empty($item->volume)) {
$numItemsWithoutVolumes++;
} else {
if ($item->libraryOwned || $item->locallyOwned) {
if (array_key_exists($item->volumeId, $volumeData)) {
if (array_key_exists($item->volumeId, $volumeData)) {
$volumeData[$item->volumeId]->addItem($item);
if ($item->libraryOwned || $item->locallyOwned) {
$volumeData[$item->volumeId]->setHasLocalItems(true);
}
}
Expand All @@ -793,6 +780,17 @@ function getPlaceHoldVolumesForm(): array {
}
}

list($interLibraryLoanType, , $homeLocation, $holdGroups) = $marcRecord->getInterLibraryLoanIntegrationInformation($relatedRecord, 'any');
if ($interLibraryLoanType != null) {
foreach ($volumeData as $key => $volumeInfo) {
if ($marcRecord->oneOrMoreHoldableItemsOwnedByPatronHoldGroups($volumeInfo->getItems(), $holdGroups, 'any', $homeLocation->code)){
$volumeInfo->setNeedsIllRequest(false);
}else{
$volumeInfo->setNeedsIllRequest(true);
}
}
}

global $library;
$interface->assign('localSystemName', $library->displayName);
$interface->assign('hasItemsWithoutVolumes', $numItemsWithoutVolumes > 0);
Expand Down Expand Up @@ -974,7 +972,15 @@ function placeHold(): array {
$return = $patron->placeItemHold($shortId, $_REQUEST['selectedItem'], $pickupBranch, $cancelDate, $pickupSublocation);
} else {
if (isset($_REQUEST['volume']) && $holdType == 'volume') {
$return = $patron->placeVolumeHold($shortId, $_REQUEST['volume'], $pickupBranch, $pickupSublocation);
if ($_REQUEST['volume'] === 'unselected') {
return [
'success' => false,
'message' => 'You must select a volume to place the hold on.',
'title' => 'Select a volume',
];
}else {
$return = $patron->placeVolumeHold($shortId, $_REQUEST['volume'], $pickupBranch, $pickupSublocation);
}
} else {
$return = $patron->placeHold($shortId, $pickupBranch, $cancelDate, $pickupSublocation);
}
Expand Down
Loading

0 comments on commit f1f57a5

Please sign in to comment.