LP1904036 Patron items out grid; shared circ grid; noncats
authorBill Erickson <berickxx@gmail.com>
Mon, 22 Feb 2021 21:19:54 +0000 (16:19 -0500)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:23 +0000 (20:13 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/circ/patron/items.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/items.component.ts
Open-ILS/src/eg2/src/app/staff/share/circ/grid.component.html
Open-ILS/src/eg2/src/app/staff/share/circ/grid.component.ts

index 5ec8605..125c734 100644 (file)
         <eg-circ-grid #checkoutsGrid></eg-circ-grid>
       </ng-template>
     </li>
-    <li ngbNavItem="other">
-      <a ngbNavLink i18n>Other/Special Circulations ({{altList.length}})</a>
-      <ng-template ngbNavContent>
-        <ng-container>
-        </ng-container>
-      </ng-template>
-    </li>
+    <ng-container *ngIf="displayAltList">
+      <li ngbNavItem="other">
+        <a ngbNavLink i18n>Other/Special Circulations ({{altList.length}})</a>
+        <ng-template ngbNavContent>
+          <ng-container>
+            <ng-container *ngIf="loading">
+              <ng-container *ngTemplateOutlet="progress"></ng-container>
+            </ng-container>
+            <eg-circ-grid #otherGrid></eg-circ-grid>
+          </ng-container>
+        </ng-template>
+      </li>
+    </ng-container>
     <li ngbNavItem="noncat">
       <a ngbNavLink i18n>Non-Cataloged Circulations</a>
       <ng-template ngbNavContent>
         <ng-container>
+          <ng-container>
+            <ng-container *ngIf="loading">
+              <ng-container *ngTemplateOutlet="progress"></ng-container>
+            </ng-container>
+            <eg-circ-grid #nonCatGrid></eg-circ-grid>
+          </ng-container>
         </ng-container>
       </ng-template>
     </li>
index 4886c0d..b68c989 100644 (file)
@@ -6,6 +6,7 @@ import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
 import {IdlObject} from '@eg/core/idl.service';
 import {OrgService} from '@eg/core/org.service';
 import {NetService} from '@eg/core/net.service';
+import {PcrudService} from '@eg/core/pcrud.service';
 import {AuthService} from '@eg/core/auth.service';
 import {PatronService} from '@eg/staff/share/patron/patron.service';
 import {PatronManagerService} from './patron.service';
@@ -18,7 +19,7 @@ import {ServerStoreService} from '@eg/core/server-store.service';
 import {AudioService} from '@eg/share/util/audio.service';
 import {CopyAlertsDialogComponent
     } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
-import {CircGridComponent} from '@eg/staff/share/circ/grid.component';
+import {CircGridComponent, CircGridEntry} from '@eg/staff/share/circ/grid.component';
 
 @Component({
   templateUrl: 'items.component.html',
@@ -35,13 +36,21 @@ export class ItemsComponent implements OnInit, AfterViewInit {
     loading = false;
     mainList: number[] = [];
     altList: number[] = [];
-    noncatDataSource: GridDataSource = new GridDataSource();
+
+    displayLost: number; // 1 | 2 | 5 | 6;
+    displayLongOverdue: number;
+    displayClaimsReturned: number;
+    fetchCheckedIn = true;
+    displayAltList = true;
 
     @ViewChild('checkoutsGrid') private checkoutsGrid: CircGridComponent;
+    @ViewChild('otherGrid') private otherGrid: CircGridComponent;
+    @ViewChild('nonCatGrid') private nonCatGrid: CircGridComponent;
 
     constructor(
         private org: OrgService,
         private net: NetService,
+        private pcrud: PcrudService,
         private auth: AuthService,
         public circ: CircService,
         private audio: AudioService,
@@ -52,10 +61,16 @@ export class ItemsComponent implements OnInit, AfterViewInit {
     ) {}
 
     ngOnInit() {
+        this.load();
     }
 
     ngAfterViewInit() {
-        setTimeout(() => this.loadTab(this.itemsTab));
+    }
+
+    load(): Promise<any> {
+        this.loading = true;
+        return this.applyDisplaySettings()
+        .then(_ => this.loadTab(this.itemsTab));
     }
 
     tabChange(evt: NgbNavChangeEvent) {
@@ -66,13 +81,66 @@ export class ItemsComponent implements OnInit, AfterViewInit {
         this.loading = true;
         let promise;
         if (name === 'checkouts') {
-            promise = this.loadCheckoutsGrid();
+            promise = this.loadMainGrid();
+        } else if (name === 'other') {
+            promise = this.loadAltGrid();
+        } else {
+            promise = this.loadNonCatGrid();
         }
 
         promise.then(_ => this.loading = false);
     }
 
-    loadCheckoutsGrid(): Promise<any> {
+    applyDisplaySettings() {
+        return this.serverStore.getItemBatch([
+            'ui.circ.items_out.lost',
+            'ui.circ.items_out.longoverdue',
+            'ui.circ.items_out.claimsreturned'
+        ]).then(sets => {
+
+            this.displayLost =
+                Number(sets['ui.circ.items_out.lost']) || 2;
+            this.displayLongOverdue =
+                Number(sets['ui.circ.items_out.longoverdue']) || 2;
+            this.displayClaimsReturned =
+                Number(sets['ui.circ.items_out.claimsreturned']) || 2;
+
+            if (this.displayLost & 4 &&
+                this.displayLongOverdue & 4 &&
+                this.displayClaimsReturned & 4) {
+
+                // all special types are configured to be hidden once
+                // checked in, so there's no need to fetch checked-in circs.
+                this.fetchCheckedIn = false;
+
+                if (this.displayLost & 1 &&
+                    this.displayLongOverdue & 1 &&
+                    this.displayClaimsReturned & 1) {
+                    // additionally, if all types are configured to display
+                    // in the main list while checked out, nothing will
+                    // ever appear in the alternate list, so we can hide
+                    // the alternate list from the UI.
+                    this.displayAltList = false;
+               }
+            }
+        });
+    }
+
+    // Determine which grid ('checkouts' or 'other') a circ should appear in.
+    promoteCircs(list: number[], displayCode: number, xactOpen?: boolean) {
+        if (xactOpen) {
+            if (1 & displayCode) { // bitflag 1 == top list
+                this.mainList = this.mainList.concat(list);
+            } else {
+                this.altList = this.altList.concat(list);
+            }
+        } else {
+            if (4 & displayCode) return;  // bitflag 4 == hide on checkin
+            this.altList = this.altList.concat(list);
+        }
+    }
+
+    getCircIds(): Promise<any> {
         this.mainList = [];
         this.altList = [];
 
@@ -82,50 +150,56 @@ export class ItemsComponent implements OnInit, AfterViewInit {
             this.auth.token(), this.patronId
         ).toPromise().then(checkouts => {
             this.mainList = checkouts.overdue.concat(checkouts.out);
-
-            // TODO promise_circs, etc.
+            this.promoteCircs(checkouts.lost, this.displayLost, true);
+            this.promoteCircs(checkouts.long_overdue, this.displayLongOverdue, true);
+            this.promoteCircs(checkouts.claims_returned, this.displayClaimsReturned, true);
         });
 
-        // TODO: fetch checked in
+        if (!this.fetchCheckedIn) { return promise; }
 
         return promise.then(_ => {
-            this.checkoutsGrid.load(this.mainList)
-            .subscribe(null, null, () => this.checkoutsGrid.reloadGrid());
+            return this.net.request(
+                'open-ils.actor',
+                'open-ils.actor.user.checked_in_with_fines.authoritative',
+                this.auth.token(), this.patronId
+            ).toPromise().then(checkouts => {
+                this.promoteCircs(checkouts.lost, this.displayLost);
+                this.promoteCircs(checkouts.long_overdue, this.displayLongOverdue);
+                this.promoteCircs(checkouts.claims_returned, this.displayClaimsReturned);
+            });
         });
     }
 
-    /*
-    function get_circ_ids() {
-        $scope.main_list = [];
-        $scope.alt_list = [];
+    loadMainGrid(): Promise<any> {
+        return this.getCircIds()
+        .then(_ => this.checkoutsGrid.load(this.mainList).toPromise())
+        .then(_ => this.checkoutsGrid.reloadGrid());
+    }
 
-        // we can fetch these in parallel
-        var promise1 = egCore.net.request(
-            'open-ils.actor',
-            'open-ils.actor.user.checked_out.authoritative',
-            egCore.auth.token(), $scope.patron_id
-        ).then(function(outs) {
-            $scope.main_list = outs.overdue.concat(outs.out);
-            promote_circs(outs.lost, display_lost, true);
-            promote_circs(outs.long_overdue, display_lo, true);
-            promote_circs(outs.claims_returned, display_cr, true);
-        });
+    loadAltGrid(): Promise<any> {
+        return this.getCircIds()
+        .then(_ => this.otherGrid.load(this.altList).toPromise())
+        .then(_ => this.otherGrid.reloadGrid());
+    }
 
-        // only fetched checked-in-with-bills circs if configured to display
-        var promise2 = !fetch_checked_in ? $q.when() : egCore.net.request(
-            'open-ils.actor',
-            'open-ils.actor.user.checked_in_with_fines.authoritative',
-            egCore.auth.token(), $scope.patron_id
-        ).then(function(outs) {
-            promote_circs(outs.lost, display_lost);
-            promote_circs(outs.long_overdue, display_lo);
-            promote_circs(outs.claims_returned, display_cr);
-        });
+    loadNonCatGrid(): Promise<any> {
 
-        return $q.all([promise1, promise2]);
-    }
-    */
+        return this.net.request(
+            'open-ils.circ',
+            'open-ils.circ.open_non_cataloged_circulation.user.batch.authoritative',
+            this.auth.token(), this.patronId)
+
+        .pipe(tap(circ => {
+            const entry: CircGridEntry = {
+                title: circ.item_type().name(),
+                dueDate: circ.duedate()
+            };
 
+            this.nonCatGrid.appendGridEntry(entry);
+        })).toPromise()
+
+        .then(_ => this.nonCatGrid.reloadGrid());
+    }
 }
 
 
index b4c1f5e..1d86af2 100644 (file)
@@ -32,6 +32,8 @@
   <eg-grid-column path="title" label="Title" i18n-label 
     [cellTemplate]="titleTemplate"></eg-grid-column>
 
+  <eg-grid-column path="author" label="Author" i18n-label></eg-grid-column>
+
   <eg-grid-column path="nonCatCount" label="Non-Cataloged Count"
     i18n-label></eg-grid-column>
 
index 7213294..9d3b91e 100644 (file)
@@ -24,6 +24,8 @@ export interface CircGridEntry {
     isbn?: string;
     copy?: IdlObject;
     circ?: IdlObject;
+    volume?: IdlObject;
+    record?: IdlObject;
     dueDate?: string;
     copyAlertCount?: number;
     nonCatCount?: number;
@@ -119,11 +121,19 @@ export class CircGridComponent implements OnInit {
         }).pipe(map(circ => {
 
             const entry = this.gridify(circ);
-            this.entries.push(entry);
+            this.appendGridEntry(entry);
             return entry;
         }));
     }
 
+
+    // Also useful for manually appending circ-like things (e.g. noncat
+    // circs) that can be massaged into CircGridEntry structs.
+    appendGridEntry(entry: CircGridEntry) {
+        if (!this.entries) { this.entries = []; }
+        this.entries.push(entry);
+    }
+
     gridify(circ: IdlObject): CircGridEntry {
 
         const entry: CircGridEntry = {
@@ -144,12 +154,19 @@ export class CircGridComponent implements OnInit {
 
         } else {
 
-            const display =
-                copy.call_number().record().wide_display_entry();
+            entry.volume = copy.call_number();
+            entry.record = entry.volume.record();
 
-            entry.title = display.title();
-            entry.author = display.author();
-            entry.isbn = display.isbn();
+            // display entries are JSON-encoded and some are lists
+            const display = entry.record.wide_display_entry();
+
+            entry.title = JSON.parse(display.title());
+            entry.author = JSON.parse(display.author());
+            entry.isbn = JSON.parse(display.isbn());
+
+            if (Array.isArray(entry.isbn)) {
+                entry.isbn = entry.isbn.join(',');
+            }
         }
 
         return entry;