From 37fa844bce84e986f84d937a39d8ed65ae10c862 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Thu, 25 Feb 2021 17:26:08 -0500 Subject: [PATCH] LP1904036 Patron alerts page; resolver service Signed-off-by: Bill Erickson Signed-off-by: Jane Sandberg Signed-off-by: Galen Charlton --- .../app/staff/circ/patron/alerts.component.html | 75 ++++++++++++++ .../src/app/staff/circ/patron/alerts.component.ts | 25 +++++ .../app/staff/circ/patron/checkout.component.ts | 4 +- .../staff/circ/patron/edit-toolbar.component.ts | 4 +- .../src/app/staff/circ/patron/edit.component.ts | 4 +- .../src/app/staff/circ/patron/holds.component.ts | 4 +- .../src/app/staff/circ/patron/items.component.ts | 4 +- .../app/staff/circ/patron/patron.component.html | 11 +- .../src/app/staff/circ/patron/patron.component.ts | 22 ++-- .../eg2/src/app/staff/circ/patron/patron.module.ts | 8 +- .../src/app/staff/circ/patron/patron.service.ts | 111 +++++++++++++++------ .../src/app/staff/circ/patron/resolver.service.ts | 40 ++++++++ .../src/app/staff/circ/patron/routing.module.ts | 8 +- .../app/staff/circ/patron/summary.component.html | 2 +- .../src/app/staff/circ/patron/summary.component.ts | 9 +- 15 files changed, 270 insertions(+), 61 deletions(-) create mode 100644 Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.html create mode 100644 Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.ts create mode 100644 Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.html new file mode 100644 index 0000000000..6b869cbb03 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.html @@ -0,0 +1,75 @@ + +
+ + + +
+ Holds available: {{context.alerts.holdsReady}} +
+ +
+ Patron account is EXPIRED. +
+ +
+ Patron account will expire soon. Please renew. +
+ +
+ Patron account is BARRED +
+ +
+ Patron account is INACTIVE +
+ +
+ Patron account retrieved with an INACTIVE card. +
+ +
+ Patron account has invalid addresses. +
+ + +
+
+
+
Alert Message
+
{{context.alerts.alertMessage}}
+
+
+
+ + +
+
+
+
Penalties
+
+
    +
  • +
    +
    + {{context.orgSn(penalty.org_unit())}} +
    +
    + {{penalty.standing_penalty().label()}} +
    {{penalty.note()}}
    +
    +
    + {{penalty.set_date() | date:'short'}} +
    +
    +
  • +
+
+
+
+
+
+ Select a tab above (for example, Check Out) to clear this alert. +
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.ts new file mode 100644 index 0000000000..814bdca3ad --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/alerts.component.ts @@ -0,0 +1,25 @@ +import {Component, OnInit, Input} from '@angular/core'; +import {Router, ActivatedRoute, ParamMap} from '@angular/router'; +import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; +import {OrgService} from '@eg/core/org.service'; +import {NetService} from '@eg/core/net.service'; +import {PatronService} from '@eg/staff/share/patron/patron.service'; +import {PatronContextService} from './patron.service'; + +@Component({ + templateUrl: 'alerts.component.html', + selector: 'eg-patron-alerts' +}) +export class PatronAlertsComponent implements OnInit { + + constructor( + private org: OrgService, + private net: NetService, + public patronService: PatronService, + public context: PatronContextService + ) {} + + ngOnInit() { + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts index b1c73caa29..62aacad01f 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts @@ -7,7 +7,7 @@ import {IdlObject} from '@eg/core/idl.service'; import {OrgService} from '@eg/core/org.service'; import {NetService} from '@eg/core/net.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; -import {PatronManagerService, CircGridEntry} from './patron.service'; +import {PatronContextService, CircGridEntry} from './patron.service'; import {CheckoutParams, CheckoutResult, CircService } from '@eg/staff/share/circ/circ.service'; import {PromptDialogComponent} from '@eg/share/dialog/prompt.component'; @@ -59,7 +59,7 @@ export class CheckoutComponent implements OnInit, AfterViewInit { private net: NetService, public circ: CircService, public patronService: PatronService, - public context: PatronManagerService, + public context: PatronContextService, private audio: AudioService ) {} diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts index c538ab7a8e..39d180d5ad 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts @@ -4,7 +4,7 @@ import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; import {OrgService} from '@eg/core/org.service'; import {NetService} from '@eg/core/net.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; -import {PatronManagerService} from './patron.service'; +import {PatronContextService} from './patron.service'; @Component({ templateUrl: 'edit-toolbar.component.html', @@ -16,7 +16,7 @@ export class EditToolbarComponent implements OnInit { private org: OrgService, private net: NetService, public patronService: PatronService, - public context: PatronManagerService + public context: PatronContextService ) {} ngOnInit() { diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts index 61012290ac..18092eceec 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts @@ -4,7 +4,7 @@ import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; import {OrgService} from '@eg/core/org.service'; import {NetService} from '@eg/core/net.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; -import {PatronManagerService} from './patron.service'; +import {PatronContextService} from './patron.service'; @Component({ templateUrl: 'edit.component.html', @@ -16,7 +16,7 @@ export class EditComponent implements OnInit { private org: OrgService, private net: NetService, public patronService: PatronService, - public context: PatronManagerService + public context: PatronContextService ) {} ngOnInit() { diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/holds.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/holds.component.ts index 79bfae663b..e7ed104e27 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/holds.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/holds.component.ts @@ -4,7 +4,7 @@ import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; import {OrgService} from '@eg/core/org.service'; import {NetService} from '@eg/core/net.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; -import {PatronManagerService} from './patron.service'; +import {PatronContextService} from './patron.service'; @Component({ templateUrl: 'holds.component.html', @@ -16,7 +16,7 @@ export class HoldsComponent implements OnInit { private org: OrgService, private net: NetService, public patronService: PatronService, - public context: PatronManagerService + public context: PatronContextService ) {} ngOnInit() { diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/items.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/items.component.ts index 692d727ead..170f84f1b4 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/items.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/items.component.ts @@ -9,7 +9,7 @@ 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'; +import {PatronContextService} from './patron.service'; import {CheckoutResult, CircService} from '@eg/staff/share/circ/circ.service'; import {PromptDialogComponent} from '@eg/share/dialog/prompt.component'; import {GridDataSource, GridColumn, GridCellTextGenerator} from '@eg/share/grid/grid'; @@ -57,7 +57,7 @@ export class ItemsComponent implements OnInit, AfterViewInit { private store: StoreService, private serverStore: ServerStoreService, public patronService: PatronService, - public context: PatronManagerService + public context: PatronContextService ) {} ngOnInit() { diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html index 736206979a..798148c5bc 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html @@ -45,7 +45,7 @@
  • - Checkout + Check Out
    @@ -99,7 +99,7 @@ (click)="false" class="nav-link" ngbDropdownToggle>Other
    Alerts + ngbDropdownItem i18n>Alerts and Messages Notes Completely Purge Account
    - - OTHER STUFF: {{altTab}} + +
    + +
    +
  • diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts index 2e2f089a72..b1b18cfe61 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.ts @@ -5,7 +5,7 @@ import {NetService} from '@eg/core/net.service'; import {AuthService} from '@eg/core/auth.service'; import {ServerStoreService} from '@eg/core/server-store.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; -import {PatronManagerService} from './patron.service'; +import {PatronContextService} from './patron.service'; import {PatronSearch, PatronSearchComponent } from '@eg/staff/share/patron/search.component'; @@ -31,7 +31,7 @@ export class PatronComponent implements OnInit, AfterViewInit { private auth: AuthService, private store: ServerStoreService, public patronService: PatronService, - public context: PatronManagerService + public context: PatronContextService ) {} ngOnInit() { @@ -71,7 +71,7 @@ export class PatronComponent implements OnInit, AfterViewInit { if (this.patronId) { if (this.patronId !== prevId) { // different patron - this.context.loadPatron(this.patronId); + this.changePatron(this.patronId); } } else { // Use the ID of the previously loaded patron. @@ -126,12 +126,22 @@ export class PatronComponent implements OnInit, AfterViewInit { const id = ids[0]; if (id !== this.patronId) { - this.patronId = id; - this.context.loadPatron(id); - return; + this.changePatron(id); } } + changePatron(id: number) { + this.patronId = id; + this.context.loadPatron(id) + .then(_ => { + if (this.context.patron && + this.context.alerts.hasAlerts() && + !this.context.patronAlertsShown()) { + this.router.navigate(['/staff/circ/patron', id, 'alerts']) + } + }); + } + // Route to checkout tab for selected patron. patronsActivated(rows: any[]) { if (rows.length !== 1) { return; } diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts index b459def2dd..8f4ed17998 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts @@ -1,5 +1,6 @@ import {NgModule} from '@angular/core'; import {PatronRoutingModule} from './routing.module'; +import {PatronResolver} from './resolver.service'; import {FmRecordEditorModule} from '@eg/share/fm-editor/fm-editor.module'; import {StaffCommonModule} from '@eg/staff/common.module'; import {HoldsModule} from '@eg/staff/share/holds/holds.module'; @@ -7,8 +8,9 @@ import {CircModule} from '@eg/staff/share/circ/circ.module'; import {HoldingsModule} from '@eg/staff/share/holdings/holdings.module'; import {BookingModule} from '@eg/staff/share/booking/booking.module'; import {PatronModule} from '@eg/staff/share/patron/patron.module'; -import {PatronManagerService} from './patron.service'; +import {PatronContextService} from './patron.service'; import {PatronComponent} from './patron.component'; +import {PatronAlertsComponent} from './alerts.component'; import {SummaryComponent} from './summary.component'; import {CheckoutComponent} from './checkout.component'; import {HoldsComponent} from './holds.component'; @@ -21,6 +23,7 @@ import {ItemsComponent} from './items.component'; @NgModule({ declarations: [ PatronComponent, + PatronAlertsComponent, SummaryComponent, CheckoutComponent, HoldsComponent, @@ -41,7 +44,8 @@ import {ItemsComponent} from './items.component'; BarcodesModule ], providers: [ - PatronManagerService + PatronResolver, + PatronContextService ] }) diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.service.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.service.ts index 3e63f8be21..4e6fcb42e0 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.service.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/patron.service.ts @@ -1,9 +1,11 @@ import {Injectable} from '@angular/core'; import {IdlObject} from '@eg/core/idl.service'; import {NetService} from '@eg/core/net.service'; +import {OrgService} from '@eg/core/org.service'; import {AuthService} from '@eg/core/auth.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; import {PatronSearch} from '@eg/staff/share/patron/search.component'; +import {StoreService} from '@eg/core/store.service'; export interface CircGridEntry { title?: string; @@ -52,30 +54,54 @@ interface PatronStats { }; } +export class PatronAlerts { + holdsReady = 0; + accountExpired = false; + accountExpiresSoon = false; + patronBarred = false; + patronInactive = false; + retrievedWithInactive = false; + invalidAddress = false; + alertMessage: string = null; + alertPenalties: IdlObject[] = []; + + hasAlerts(): boolean { + return ( + this.holdsReady > 0 || + this.accountExpired || + this.accountExpiresSoon || + this.patronBarred || + this.patronInactive || + this.retrievedWithInactive || + this.invalidAddress || + this.alertMessage !== null || + this.alertPenalties.length > 0 + ); + } +} + @Injectable() -export class PatronManagerService { +export class PatronContextService { patron: IdlObject; patronStats: PatronStats; + alerts: PatronAlerts; - // Value for YAOUS circ.do_not_tally_claims_returned - noTallyClaimsReturned = false; - - // Value for YAOUS circ.tally_lost - tallyLost = false; + noTallyClaimsReturned = false; // circ.do_not_tally_claims_returned + tallyLost = false; // circ.tally_lost loaded = false; - accountExpired = false; - accountExpiresSoon = false; - lastPatronSearch: PatronSearch; + searchBarcode: string = null; // These should persist tab changes checkouts: CircGridEntry[] = []; constructor( + private store: StoreService, private net: NetService, + private org: OrgService, private auth: AuthService, public patronService: PatronService ) {} @@ -84,6 +110,7 @@ export class PatronManagerService { this.loaded = false; this.patron = null; this.checkouts = []; + this.alerts = new PatronAlerts(); return this.net.request( 'open-ils.actor', @@ -91,30 +118,10 @@ export class PatronManagerService { this.auth.token(), id, PATRON_FLESH_FIELDS).toPromise() .then(p => this.patron = p) .then(_ => this.getPatronStats(id)) - .then(_ => this.setExpires()) + .then(_ => this.compileAlerts()) .then(_ => this.loaded = true); } - setExpires(): Promise { - this.accountExpired = false; - this.accountExpiresSoon = false; - - // When quickly navigating patron search results it's possible - // for this.patron to be cleared right before this function - // is called. Exit early instead of making an unneeded call. - // For this func. in particular a nasty JS error is thrown. - if (!this.patron) { return Promise.resolve(); } - - return this.patronService.testExpire(this.patron) - .then(value => { - if (value === 'expired') { - this.accountExpired = true; - } else if (value === 'soon') { - this.accountExpiresSoon = true; - } - }); - } - getPatronStats(id: number): Promise { // When quickly navigating patron search results it's possible @@ -163,6 +170,50 @@ export class PatronManagerService { } }); } + + patronAlertsShown(): boolean { + if (!this.patron) { return false; } + const shown = this.store.getSessionItem('eg.circ.last_alerted_patron'); + if (shown === this.patron.id()) { return true; } + this.store.setSessionItem('eg.circ.last_alerted_patron', this.patron.id()); + return false; + } + + compileAlerts(): Promise { + + // User navigated to a different patron mid-data load. + if (!this.patron) { return Promise.resolve(); } + + this.alerts.holdsReady = this.patronStats.holds.ready; + this.alerts.patronBarred = this.patron.barred() === 't'; + this.alerts.patronInactive = this.patron.active() === 'f'; + this.alerts.invalidAddress = this.patron.addresses() + .filter(a => a.valid() === 'f').length > 0; + this.alerts.alertMessage = this.patron.alert_message(); + this.alerts.alertPenalties = this.patron.standing_penalties() + .filter(p => p.standing_penalty().staff_alert() === 't'); + + if (this.searchBarcode) { + const card = this.patron.cards() + .filter(c => c.barcode() === this.searchBarcode)[0]; + this.alerts.retrievedWithInactive = card && card.active() === 'f'; + this.searchBarcode = null; + } + + return this.patronService.testExpire(this.patron) + .then(value => { + if (value === 'expired') { + this.alerts.accountExpired = true; + } else if (value === 'soon') { + this.alerts.accountExpiresSoon = true; + } + }); + } + + orgSn(orgId: number): string { + const org = this.org.get(orgId); + return org ? org.shortname() : ''; + } } diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts new file mode 100644 index 0000000000..8ddef06f40 --- /dev/null +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/resolver.service.ts @@ -0,0 +1,40 @@ +import {Injectable} from '@angular/core'; +import {Router, Resolve, RouterStateSnapshot, + ActivatedRouteSnapshot} from '@angular/router'; +import {ServerStoreService} from '@eg/core/server-store.service'; +import {NetService} from '@eg/core/net.service'; +import {OrgService} from '@eg/core/org.service'; +import {AuthService} from '@eg/core/auth.service'; +import {PatronContextService} from './patron.service'; + + +@Injectable() +export class PatronResolver implements Resolve> { + + constructor( + private store: ServerStoreService, + private context: PatronContextService + ) {} + + resolve( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Promise { + + return this.fetchSettings(); + } + + fetchSettings(): Promise { + + return this.store.getItemBatch([ + 'eg.circ.patron.summary.collapse', + 'circ.do_not_tally_claims_returned', + 'circ.tally_lost' + + ]).then(settings => { + this.context.noTallyClaimsReturned = + settings['circ.do_not_tally_claims_returned']; + this.context.tallyLost = settings['circ.tally_lost']; + }); + } +} + diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts index de04742aef..06c95d8d3e 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/routing.module.ts @@ -2,6 +2,7 @@ import {NgModule} from '@angular/core'; import {RouterModule, Routes} from '@angular/router'; import {PatronComponent} from './patron.component'; import {BcSearchComponent} from './bcsearch.component'; +import {PatronResolver} from './resolver.service'; const routes: Routes = [{ path: '', @@ -13,7 +14,8 @@ const routes: Routes = [{ import('./event-log/event-log.module').then(m => m.EventLogModule) }, { path: 'search', - component: PatronComponent + component: PatronComponent, + resolve: {resolver : PatronResolver} }, { path: 'bcsearch', component: BcSearchComponent @@ -21,8 +23,12 @@ const routes: Routes = [{ path: 'bcsearch/:barcode', component: BcSearchComponent }, { + path: ':id', + redirectTo: ':id/checkout' + }, { path: ':id/:tab', component: PatronComponent, + resolve: {resolver : PatronResolver} }]; @NgModule({ diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html index cdeaf97875..4b6e162794 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.html @@ -19,7 +19,7 @@
    Home Library
    -
    {{orgSn(context.patron.home_ou())}}
    +
    {{context.orgSn(context.patron.home_ou())}}
    Net Access
    diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts index 90da1f9230..aac3c49025 100644 --- a/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/circ/patron/summary.component.ts @@ -4,7 +4,7 @@ import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap'; import {OrgService} from '@eg/core/org.service'; import {NetService} from '@eg/core/net.service'; import {PatronService} from '@eg/staff/share/patron/patron.service'; -import {PatronManagerService} from './patron.service'; +import {PatronContextService} from './patron.service'; @Component({ templateUrl: 'summary.component.html', @@ -17,15 +17,10 @@ export class SummaryComponent implements OnInit { private org: OrgService, private net: NetService, public patronService: PatronService, - public context: PatronManagerService + public context: PatronContextService ) {} ngOnInit() { } - - orgSn(orgId: number): string { - const org = this.org.get(orgId); - return org ? org.shortname() : ''; - } } -- 2.11.0