gui for reset reason entries user/lew/retargeter-hold-notes-3-9-1
authorLlewellyn Marshall <llewellyn.marshall@ncdcr.gov>
Wed, 8 Mar 2023 18:56:23 +0000 (13:56 -0500)
committerLlewellyn Marshall <llewellyn.marshall@ncdcr.gov>
Fri, 17 Mar 2023 20:06:18 +0000 (16:06 -0400)
fixed perms on fm_idl for ahrrre and order by statement in hold.js added limit of 500 notes.

Open-ILS/examples/fm_IDL.xml
Open-ILS/src/perlmods/lib/OpenILS/Application/Circ/Holds.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/HoldTargeter.pm
Open-ILS/src/sql/Pg/upgrade/xxxx.hold_reset_reasons.sql
Open-ILS/src/templates/staff/circ/share/t_hold_details.tt2
Open-ILS/web/js/ui/default/staff/circ/services/holds.js

index 0f22734..bd3b786 100644 (file)
@@ -6811,6 +6811,7 @@ SELECT  usr,
                        <field reporter:label="Requesting Library" name="request_lib" reporter:datatype="org_unit"/>
                        <field reporter:label="Request Date/Time" name="request_time" reporter:datatype="timestamp"/>
                        <field reporter:label="Requesting User" name="requestor" reporter:datatype="link"/>
+            <field reporter:label="Reset Entries" name="reset_entries" oils_persist:virtual="true" reporter:datatype="link"/>
                        <field reporter:label="Item Selection Depth" name="selection_depth" />
                        <field reporter:label="Selection Locus" name="selection_ou" reporter:datatype="org_unit"/>
                        <field reporter:label="Target Object ID" name="target" reporter:datatype="link"/>
@@ -6846,6 +6847,7 @@ SELECT  usr,
                        <link field="request_lib" reltype="has_a" key="id" map="" class="aou"/>
                        <link field="transit" reltype="might_have" key="hold" map="" class="ahtc"/>
                        <link field="notifications" reltype="has_many" key="hold" map="" class="ahn"/>
+                       <link field="reset_entries" reltype="has_many" key="hold" map="" class="ahrrre"/>
                        <link field="eligible_copies" reltype="has_many" key="hold" map="target_copy" class="ahcm"/>
                        <link field="bib_rec" reltype="might_have" key="id" map="" class="rhrr"/>
                        <link field="cancel_cause" reltype="might_have" key="id" map="" class="ahrcc"/>
@@ -15429,15 +15431,20 @@ SELECT  usr,
         </fields>
     </class>
     
-       <class id="ahrrr" controller="open-ils.cstore" oils_obj:fieldmapper="action::hold_request_reset_reason" oils_persist:tablename="action.hold_request_reset_reason" reporter:label="Hold Request Reset Reason" oils_persist:field_safe="true">
+       <class id="ahrrr" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::hold_request_reset_reason" oils_persist:tablename="action.hold_request_reset_reason" reporter:label="Hold Request Reset Reason" oils_persist:field_safe="true">
                <fields oils_persist:primary="id" oils_persist:sequence="action.hold_request_reset_reason_id_seq">
                        <field name="id" reporter:selector="name" reporter:datatype="id" reporter:label="ID"/>
                        <field name="name"  reporter:datatype="text" oils_persist:i18n="true" reporter:label="Name"/>
             <field name="manual" reporter:datatype="bool" reporter:label="Manual"/>
                </fields>
+               <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+                       <actions>
+                               <retrieve/>
+                       </actions>
+               </permacrud>
        </class>
 
-       <class id="ahrrre" controller="open-ils.cstore" oils_obj:fieldmapper="action::hold_request_reset_reason_entry" oils_persist:tablename="action.hold_request_reset_reason_entry" reporter:label="Hold Request Reset Reason Entry" oils_persist:field_safe="true">
+       <class id="ahrrre" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="action::hold_request_reset_reason_entry" oils_persist:tablename="action.hold_request_reset_reason_entry" reporter:label="Hold Request Reset Reason Entry" oils_persist:field_safe="true">
                <fields oils_persist:primary="id" oils_persist:sequence="action.hold_request_reset_reason_entry_id_seq">
                        <field name="id" reporter:datatype="id" reporter:label="ID"/>
             <field name="hold" reporter:label="ID" reporter:datatype="link"/>
@@ -15451,10 +15458,17 @@ SELECT  usr,
                <links>
             <link field="hold" reltype="has_a" key="id" map="" class="ahr"/>        
             <link field="reset_reason" reltype="has_a" key="id" map="" class="ahrrr"/>        
-            <link field="requestor" reltype="might_have" key="id" map="" class="au"/>    
-            <link field="requestor_workstation" reltype="might_have" key="id" map="" class="aws"/>            
-            <link field="previous_copy" reltype="might_have" key="id" map="" class="acp"/>        
+            <link field="requestor" reltype="has_a" key="id" map="" class="au"/>    
+            <link field="requestor_workstation" reltype="has_a" key="id" map="" class="aws"/>            
+            <link field="previous_copy" reltype="has_a" key="id" map="" class="acp"/>        
         </links>
+        <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+            <actions>
+                               <retrieve permission="VIEW_HOLD">
+                    <context link="pickup_lib" field="hold"/>
+                </retrieve>
+            </actions>
+        </permacrud>
        </class>
 
 
index e16f44e..ad16c35 100644 (file)
@@ -2245,7 +2245,7 @@ __PACKAGE__->register_method(
 
 sub create_reset_reason_entry
 {
-    my($self, $conn, $auth, $hold, $reset_reason, $note) = @_;
+    my($self, $conn, $auth, $hold, $reset_reason, $note, $previous_copy) = @_;
     my $e = new_editor(authtoken => $auth, xact => 1);
     #checkauth to set the requestor (if available)
     $e->checkauth;
@@ -2259,7 +2259,13 @@ sub create_reset_reason_entry
     for my $holdid (@holds){
         try{
             my ($hold, $evt) = $U->fetch_hold($holdid);  
-            _create_reset_reason_entry($e, $hold, $reset_reason, $note) unless $evt;
+            _create_reset_reason_entry(
+                $e, 
+                $hold, 
+                $reset_reason, 
+                $note, 
+                $previous_copy) 
+            unless $evt;
         }
         catch Error with{
             $logger->error("holds: create reset reason failed with ".shift());
@@ -2280,7 +2286,7 @@ sub _create_reset_reason_entry
     $entry->reset_reason($reset_reason);
     $entry->reset_time('now');
     $entry->previous_copy($last_copy);
-    $entry->note($note);
+    $entry->note($note) if defined $note;
     $entry->requestor($e->requestor->id) if defined $e->requestor;
     $entry->requestor_workstation($e->requestor->wsid)  if defined $e->requestor;
     $e->create_action_hold_request_reset_reason_entry($entry) or return $e->die_event;
index 7305015..ad7ed5b 100644 (file)
@@ -109,8 +109,11 @@ sub hold_targeter {
                     $single->editor()->authtoken,
                     $hold_id,
                     OILS_HOLD_TIMED_OUT,
+                    undef,
                     $single->{previous_copy_id}
-                ) unless defined $args->{hold};
+                ) unless defined 
+                            $args->{hold} || 
+                            $single->{previous_copy_id} == $single->hold->current_copy;
             } catch Error with {
                 $logger->error(
                     "hold-targeter: create reset reason failed with ".shift()
index 1de4750..0f8e294 100644 (file)
@@ -53,6 +53,8 @@ WITH (
   OIDS=FALSE
 );
 
+CREATE INDEX ahrrre_hold ON action.hold_request_reset_reason_entry (hold);
+
 INSERT INTO config.global_flag (name, label, enabled)
     VALUES (
         'circ.holds.retarget_previous_targets_interval',
index ece4719..76d540b 100644 (file)
       [% l('Staff Notifications') %]
     </a>
   </li>
+  <li ng-class="{active : detail_tab == 'resets'}">
+    <a href ng-click="show_resets_tab()">
+      [% l('Reset Entries') %]
+    </a>
+  </li>
 </ul>
 <div class="tab-content">
   <div class="tab-pane active">
     </div><!-- notes tab content -->
 
     <div ng-if="detail_tab == 'notify'">
+        <div class="btn-group column-picker">
+            <!-- first page -->
+            <button type="button" class="btn btn-default" 
+              
+              ng-class="{disabled : on_first_rs_page()}" 
+              ng-disabled="on_first_rs_page()"
+              ng-click="first_rs_page()"
+              title="[% l('Start') %]">
+                <span class="glyphicon glyphicon-fast-backward"></span>
+            </button>
+
+            <!-- previous page -->
+            <button type="button" class="btn btn-default" 
       
+              ng-class="{disabled : on_first_rs_page()}"
+              ng-disabled="on_first_rs_page()"
+              ng-click="decrement_rs_page()"
+              title="[% l('Previous Page') %]">
+                <span class="glyphicon glyphicon-backward"></span>
+            </button>
+
+            <!-- next page -->
+            <!-- todo: paging needs a total count value to be fully functional -->
+            <button type="button" class="btn btn-default" 
+
+              ng-class="{disabled : !has_next_rs_page()}"
+              ng-disabled="!has_next_rs_page()"
+              ng-click="increment_rs_page()"
+              title="[% l('Next Page') %]">
+                <span class="glyphicon glyphicon-forward"></span>
+            </button>
+        </div>      
       <button class="btn btn-default" ng-click="new_notification()">
         [% l('Add Record of Notification') %]
       </button>
 
       <div class="row pad-vert" 
-          ng-repeat="notify in hold.notifications()">
+          ng-repeat="notify in filteredResets">
         <div class="col-md-12">
           <div class="row">
             <div class="col-md-6 strong-text">{{notify.method()}}</div>
         </div>
       </div>
     </div><!-- notes tab content -->
+    
+    <div ng-if="detail_tab == 'resets'">
+        <div class="btn-group column-picker">
+            <!-- change order -->           
+            <button type="button" class="btn btn-default" 
+              ng-click="reverse_reset_order()"
+              title="[% l('Reverse Order') %]">
+                <span class="glyphicon {{reverseResetOrder ? 'glyphicon-chevron-up' : 'glyphicon-chevron-down' }}"></span>
+            </button>
+            
+            <!-- first page -->
+            <button type="button" class="btn btn-default" 
+              
+              ng-class="{disabled : on_first_rs_page()}" 
+              ng-disabled="on_first_rs_page()"
+              ng-click="first_rs_page()"
+              title="[% l('Start') %]">
+                <span class="glyphicon glyphicon-fast-backward"></span>
+            </button>
+
+            <!-- previous page -->
+            <button type="button" class="btn btn-default" 
+      
+              ng-class="{disabled : on_first_rs_page()}"
+              ng-disabled="on_first_rs_page()"
+              ng-click="decrement_rs_page()"
+              title="[% l('Previous Page') %]">
+                <span class="glyphicon glyphicon-backward"></span>
+            </button>
+
+            <!-- next page -->
+            <!-- todo: paging needs a total count value to be fully functional -->
+            <button type="button" class="btn btn-default" 
+
+              ng-class="{disabled : !has_next_rs_page()}"
+              ng-disabled="!has_next_rs_page()"
+              ng-click="increment_rs_page()"
+              title="[% l('Next Page') %]">
+                <span class="glyphicon glyphicon-forward"></span>
+            </button>
+        </div>
+      <div ng-show="!resetsLoaded" style="text-align:center;">      
+        <img src='[% ctx.media_prefix %]/opac/images/progressbar_green.gif[% ctx.cache_key %]' style='height:32px;width:32px;' alt='[% l("Search In Progress") %]'/>
+      </div>      
+      <div class="flex-container-striped flex-container-bordered" ng-show="resetsLoaded && filteredResets.length">
+        <div class="flex-row">
+            <div class="flex-cell strong-text">Time</div>
+            <div class="flex-cell strong-text">Reason</div>
+            <div class="flex-cell strong-text">Requestor</div>
+            <div class="flex-cell strong-text">Note</div>
+            <div class="flex-cell strong-text">Previous Copy</div>
+        </div>
+        <div class="flex-row" ng-repeat="reset in filteredResets">
+            <div class="flex-cell">{{reset.reset_time() | date:$root.egDateAndTimeFormat}}</div>
+            <div class="flex-cell">{{reset.reset_reason().name()}}</div>
+            <div class="flex-cell">{{reset.requestor().usrname()}}</div>
+            <div class="flex-cell">{{reset.note()}}</div>
+            <div class="flex-cell">
+                <a href="./cat/item/{{reset.previous_copy().id()}}" target="_self">
+                        {{reset.previous_copy().barcode()}}
+                </a>
+            </div>
+        </div>
+      </div>
+      <div class="flex-container-striped flex-container-bordered" ng-show="resetsLoaded && !filteredResets.length">
+        <div class="flex-row">
+        [%- l('No reset entries found for this hold.') -%]
+        </div>
+      </div>
+    </div><!-- resets tab content -->
 
   </div><!-- tab pane -->
 </div><!-- tab-content -->
index 251ceb2..3cfd3b1 100644 (file)
@@ -815,7 +815,7 @@ function($window , $location , $timeout , egCore , egHolds , egCirc) {
             // if set, called whenever hold details are retrieved.  The
             // argument is the hold blob returned from hold.details.retrieve
             holdRetrieved : '=',
-            showPatron : '='
+            showPatron : '=',
         },
         controller : [
                     '$scope','$uibModal','egCore','egHolds','egCirc',
@@ -858,7 +858,85 @@ function($window , $location , $timeout , egCore , egHolds , egCirc) {
 
                     });
                 }
+                
+                $scope.resetPage = 1;
+                $scope.resetsPerPage = 10;
+                $scope.maximumPages = 25;
+                $scope.resetsLoaded = false;
+                $scope.reverseResetOrder = false;
+                
+                $scope.show_resets_tab = function() {
+                    $scope.detail_tab = 'resets';
+                    egCore.pcrud.search('ahrrre',
+                        {hold : $scope.hold.id()}, 
+                        {
+                            flesh : 1, 
+                            flesh_fields : {ahrrre : ['reset_reason','requestor','previous_copy']},                          
+                            limit : $scope.resetsPerPage * $scope.maximumPages
+                        },
+                        {atomic : true}
+                    ).then(function(ents) {
+                        // sort the reset notes by date
+                        ents.sort(
+                            function(a,b){
+                                return Date.parse(a.reset_time()) - Date.parse(b.reset_time());
+                            }
+                        );
+                        $scope.hold.reset_entries(ents);
+                        $scope.filter_resets();
+                        $scope.resetsLoaded = true;
+                    });
+                }
+                
+                $scope.filter_resets = function(){
+                    if(
+                        typeof($scope.hold) === 'undefined' || 
+                        typeof($scope.hold.reset_entries) === 'undefined' || 
+                        $scope.hold.reset_entries() === null
+                    )
+                        return;
+                    var begin = (($scope.resetPage - 1) * $scope.resetsPerPage),
+                        end = begin + $scope.resetsPerPage;
+                    $scope.filteredResets = $scope.hold
+                                                .reset_entries()
+                                                .slice(begin,end);
+                }
+                
+                $scope.reverse_reset_order = function(){
+                    $scope.hold.reset_entries().reverse()
+                    $scope.reverseResetOrder = !$scope.reverseResetOrder;
+                    $scope.first_rs_page();
+                }
+                
+                $scope.on_first_rs_page = function(){
+                    return $scope.resetPage == 1;
+                } 
+                
+                $scope.has_next_rs_page = function(){
+                    return $scope.resetPage < $scope.max_rs_pages();                
+                }
+                
+                $scope.max_rs_pages = function(){
+                    if(typeof($scope.hold.reset_entries) === 'undefined' || $scope.hold.reset_entries() === null)
+                        return 0;
+                    return $scope.hold.reset_entries().length/$scope.resetsPerPage;
+                }
+                
+                $scope.first_rs_page = function() {
+                    $scope.resetPage = 1;
+                }  
+                
+                $scope.increment_rs_page = function() {
+                    $scope.resetPage++;
+                }
 
+                $scope.decrement_rs_page = function() {
+                    $scope.resetPage--;
+                }  
+                
+                $scope.$watch('resetPage',$scope.filter_resets);
+                $scope.$watch('reverseResetOrder',$scope.filter_resets);
+                
                 $scope.show_notify_tab = function() {
                     $scope.detail_tab = 'notify';
                     egCore.pcrud.search('ahn',