LP1869898 Angular staff cat place hold from patron
authorBill Erickson <berickxx@gmail.com>
Mon, 8 Jun 2020 18:57:37 +0000 (14:57 -0400)
committerGalen Charlton <gmc@equinoxinitiative.org>
Thu, 13 Aug 2020 15:16:29 +0000 (11:16 -0400)
The place hold button in the patron holds list now takes staff to the
Angular catalog for holds placement.  A banner is displayed along the
top of the catalog to indicate which patron the hold is for and to
provide a link back to the patron's holds list.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <sandbej@linnbenton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxinitiative.org>
Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.html
Open-ILS/src/eg2/src/app/staff/catalog/catalog.component.ts
Open-ILS/src/eg2/src/app/staff/catalog/catalog.service.ts
Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts
Open-ILS/src/eg2/src/app/staff/share/patron/patron.service.ts
Open-ILS/src/templates/staff/circ/patron/t_holds.tt2
Open-ILS/web/js/ui/default/staff/circ/patron/holds.js

index af1d604..53c4622 100644 (file)
@@ -1,5 +1,23 @@
 <eg-staff-banner bannerText="Staff Catalog (Experimental)" i18n-bannerText>
 </eg-staff-banner>
+
+<ng-container *ngIf="holdForUser()">
+  <div class="row border border-info mb-2 pt-1 pb-1">
+    <div class="col-lg-10 offset-lg-1 d-flex justify-content-center">
+      <span class="mt-2" i18n>
+        Placing hold for patron 
+        <a href="/eg/staff/circ/patron/{{holdForUser().id()}}/holds">
+          {{holdForUser().family_name()}}, {{holdForUser().first_given_name()}}
+        </a>.
+      </span>
+    </div>
+    <div class="col-lg-1">
+      <button class="btn btn-info btn-sm" 
+        (click)="clearHoldPatron()" i18n>Clear</button>
+    </div>
+  </div>
+</ng-container>
+
 <!-- search form sits atop every catalog page -->
 <eg-catalog-search-form></eg-catalog-search-form>
 
index f9fcf6d..5ce3712 100644 (file)
@@ -1,4 +1,5 @@
 import {Component, OnInit} from '@angular/core';
+import {IdlObject} from '@eg/core/idl.service';
 import {StaffCatalogService} from './catalog.service';
 import {BasketService} from '@eg/share/catalog/basket.service';
 
@@ -18,5 +19,16 @@ export class CatalogComponent implements OnInit {
         // reset and updated as needed to apply new search parameters.
         this.staffCat.createContext();
     }
+
+    // Returns the 'au' object for the patron who we are
+    // trying to place a hold for.
+    holdForUser(): IdlObject {
+        return this.staffCat.holdForUser;
+    }
+
+    clearHoldPatron() {
+        this.staffCat.holdForUser = null;
+        this.staffCat.holdForBarcode = null;
+    }
 }
 
index e46c1b4..95912a4 100644 (file)
@@ -6,6 +6,7 @@ import {CatalogService} from '@eg/share/catalog/catalog.service';
 import {CatalogUrlService} from '@eg/share/catalog/catalog-url.service';
 import {CatalogSearchContext} from '@eg/share/catalog/search-context';
 import {BibRecordSummary} from '@eg/share/catalog/bib-record.service';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
 
 /**
  * Shared bits needed by the staff version of the catalog.
@@ -27,6 +28,11 @@ export class StaffCatalogService {
     // Default search tab
     defaultTab: string;
 
+    // Patron barcode we hope to place a hold for.
+    holdForBarcode: string;
+    // User object for above barcode.
+    holdForUser: IdlObject;
+
     // Cache the currently selected detail record (i.g. catalog/record/123)
     // summary so the record detail component can avoid duplicate fetches
     // during record tab navigation.
@@ -37,6 +43,7 @@ export class StaffCatalogService {
         private route: ActivatedRoute,
         private org: OrgService,
         private cat: CatalogService,
+        private patron: PatronService,
         private catUrl: CatalogUrlService
     ) { }
 
@@ -48,6 +55,13 @@ export class StaffCatalogService {
         this.searchContext =
             this.catUrl.fromUrlParams(this.route.snapshot.queryParamMap);
 
+        this.holdForBarcode = this.route.snapshot.queryParams['holdForBarcode'];
+
+        if (this.holdForBarcode) {
+            this.patron.getByBarcode(this.holdForBarcode)
+            .then(user => this.holdForUser = user);
+        }
+
         this.searchContext.org = this.org; // service, not searchOrg
         this.searchContext.isStaff = true;
         this.applySearchDefaults();
index c1640b0..687783e 100644 (file)
@@ -13,6 +13,7 @@ import {StaffCatalogService} from '../catalog.service';
 import {HoldsService, HoldRequest,
     HoldRequestTarget} from '@eg/staff/share/holds/holds.service';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PatronSearchDialogComponent
   } from '@eg/staff/share/patron/search-dialog.component';
 
@@ -81,6 +82,7 @@ export class HoldComponent implements OnInit {
         private cat: CatalogService,
         private staffCat: StaffCatalogService,
         private holds: HoldsService,
+        private patron: PatronService,
         private perm: PermService
     ) {
         this.holdContexts = [];
@@ -93,6 +95,11 @@ export class HoldComponent implements OnInit {
         this.holdTargets = this.route.snapshot.queryParams['target'];
         this.holdFor = this.route.snapshot.queryParams['holdFor'] || 'patron';
 
+        if (this.staffCat.holdForBarcode) {
+            this.holdFor = 'patron';
+            this.userBarcode = this.staffCat.holdForBarcode;
+        }
+
         if (!Array.isArray(this.holdTargets)) {
             this.holdTargets = [this.holdTargets];
         }
@@ -107,7 +114,7 @@ export class HoldComponent implements OnInit {
             return ctx;
         });
 
-        if (this.holdFor === 'staff') {
+        if (this.holdFor === 'staff' || this.userBarcode) {
             this.holdForChanged();
         }
 
@@ -245,25 +252,18 @@ export class HoldComponent implements OnInit {
 
         this.user = null;
         this.currentUserBarcode = this.userBarcode;
+        this.getUser();
+    }
 
-        this.net.request(
-            'open-ils.actor',
-            'open-ils.actor.get_barcodes',
-            this.auth.token(), this.auth.user().ws_ou(),
-            'actor', this.userBarcode
-        ).subscribe(barcodes => {
-
-            // Use the first successful barcode response.
-            // TODO: What happens when there are multiple responses?
-            // Use for-loop for early exit since we have async
-            // action within the loop.
-            for (let i = 0; i < barcodes.length; i++) {
-                const bc = barcodes[i];
-                if (!this.evt.parse(bc)) {
-                    this.getUser(bc.id);
-                    break;
-                }
-            }
+    getUser(id?: number) {
+        const flesh = {flesh: 1, flesh_fields: {au: ['settings']}};
+
+        const promise = id ? this.patron.getById(id, flesh) :
+            this.patron.getByBarcode(this.userBarcode);
+
+        promise.then(user => {
+            this.user = user;
+            this.applyUserSettings();
         });
     }
 
@@ -274,14 +274,6 @@ export class HoldComponent implements OnInit {
         this.pickupLib = this.requestor.ws_ou();
     }
 
-    getUser(id: number) {
-        this.pcrud.retrieve('au', id, {flesh: 1, flesh_fields: {au: ['settings']}})
-        .subscribe(user => {
-            this.user = user;
-            this.applyUserSettings();
-        });
-    }
-
     applyUserSettings() {
         if (!this.user || !this.user.settings()) { return; }
 
index 12fd1e1..e50fbb2 100644 (file)
@@ -1,5 +1,8 @@
 import {Injectable} from '@angular/core';
+import {IdlObject} from '@eg/core/idl.service';
 import {NetService} from '@eg/core/net.service';
+import {EventService} from '@eg/core/event.service';
+import {PcrudService} from '@eg/core/pcrud.service';
 import {AuthService} from '@eg/core/auth.service';
 import {Observable} from 'rxjs';
 
@@ -8,6 +11,8 @@ import {Observable} from 'rxjs';
 export class PatronService {
     constructor(
         private net: NetService,
+        private evt: EventService,
+        private pcrud: PcrudService,
         private auth: AuthService
     ) {}
 
@@ -19,5 +24,28 @@ export class PatronService {
            'actor', barcode.trim());
     }
 
+    getByBarcode(barcode: string, pcrudOps?: any): Promise<IdlObject> {
+        return this.bcSearch(barcode).toPromise()
+        .then(barcodes => {
+
+            // Use the first successful barcode response.
+            // TODO: What happens when there are multiple responses?
+            // Use for-loop for early exit since we have async
+            // action within the loop.
+            for (let i = 0; i < barcodes.length; i++) {
+                const bc = barcodes[i];
+                if (!this.evt.parse(bc)) {
+                    return this.getById(bc.id);
+                }
+            }
+
+            return null;
+        });
+    }
+
+    getById(id: number, pcrudOps?: any): Promise<IdlObject> {
+        return this.pcrud.retrieve('au', id, pcrudOps).toPromise();
+    }
+
 }
 
index 5ccd762..a5499ac 100644 (file)
@@ -32,7 +32,9 @@
 
 <!-- catalog view for holds placement -->
 
+<!-- Replaced with Angular catalog link
 <div ng-if="placing_hold">
   <eg-embed-frame url="catalog_url" handlers="handlers" 
     onchange="handle_page"></eg-embed-frame>
 </div>
+-->
index c3ce75b..798ff73 100644 (file)
@@ -145,7 +145,10 @@ function($scope,  $q,  $routeParams,  egCore,  egUser,  patronSvc,
     }
 
     $scope.place_hold = function() {
-        $location.path($location.path() + '/create');
+        $window.location.href = '/eg2/staff/catalog?holdForBarcode=' + 
+            encodeURIComponent(patronSvc.current.card().barcode());
+
+        //$location.path($location.path() + '/create');
     }
 
     // when the detail hold is fetched (and updated), update the bib