-<div *ngIf="context.alerts">
+<div *ngIf="alerts()">
<img class="mt-n4" src="/images/stop_sign.png"/>
- <div class="alert alert-info" *ngIf="context.alerts.holdsReady > 0" i18n>
- Holds available: {{context.alerts.holdsReady}}
+ <div class="alert alert-info" *ngIf="alerts().holdsReady > 0" i18n>
+ Holds available: {{alerts().holdsReady}}
</div>
- <div class="mt-2 alert alert-warning" *ngIf="context.alerts.accountExpired" i18n>
+ <div class="mt-2 alert alert-warning" *ngIf="alerts().accountExpired" i18n>
Patron account is EXPIRED.
</div>
- <div class="mt-2 alert alert-warning" *ngIf="context.alerts.accountExpiresSoon" i18n>
+ <div class="mt-2 alert alert-warning" *ngIf="alerts().accountExpiresSoon" i18n>
Patron account will expire soon. Please renew.
</div>
- <div class="mt-2 alert alert-danger" *ngIf="context.alerts.patronBarred" i18n>
+ <div class="mt-2 alert alert-danger" *ngIf="alerts().patronBarred" i18n>
Patron account is BARRED
</div>
- <div class="mt-2 alert alert-warning" *ngIf="context.alerts.patronInactive" i18n>
+ <div class="mt-2 alert alert-warning" *ngIf="alerts().patronInactive" i18n>
Patron account is INACTIVE
</div>
- <div class="mt-2 alert alert-warning" *ngIf="context.alerts.retrievedWithInactive" i18n>
+ <div class="mt-2 alert alert-warning" *ngIf="alerts().retrievedWithInactive" i18n>
Patron account retrieved with an INACTIVE card.
</div>
- <div class="mt-2 alert alert-warning" *ngIf="context.alerts.invalidAddress" i18n>
+ <div class="mt-2 alert alert-warning" *ngIf="alerts().invalidAddress" i18n>
Patron account has invalid addresses.
</div>
<!-- alert message -->
- <div class="row" *ngIf="context.alerts.alertMessage">
+ <div class="row" *ngIf="alerts().alertMessage">
<div class="col-lg-6 offset-lg-3">
<div class="card">
<div class="card-header" i18n>Alert Message</div>
- <div class="card-body">{{context.alerts.alertMessage}}</div>
+ <div class="card-body">{{alerts().alertMessage}}</div>
</div>
</div>
</div>
<!-- penalties -->
- <div class="row" *ngIf="context.alerts.alertPenalties.length">
+ <div class="row" *ngIf="alerts().alertPenalties.length">
<div class="col-lg-12">
<div class="card">
<div class="card-header" i18n>Penalties</div>
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="list-group-item"
- *ngFor="let penalty of context.alerts.alertPenalties">
+ *ngFor="let penalty of alerts().alertPenalties">
<div class="row">
<div class="col-lg-2">
{{context.orgSn(penalty.org_unit())}}
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 {PatronService, PatronAlerts} from '@eg/staff/share/patron/patron.service';
import {PatronContextService} from './patron.service';
@Component({
ngOnInit() {
}
+
+ alerts(): PatronAlerts {
+ return this.context.summary ? this.context.summary.alerts : null;
+ }
}
}
patron(): IdlObject {
- return this.context.patron;
+ return this.context.summary ? this.context.summary.patron : null;
}
selectedPaymentInfo(): {owed: number, billed: number, paid: number} {
}
const pending = this.pendingPayment();
- const prevBalance = this.context.patronStats.fines.balance_owed;
+ const prevBalance = this.context.summary.stats.fines.balance_owed;
const newBalance = (prevBalance * 100 - pending * 100) / 100;
const context = {
collectParams(): Promise<CheckoutParams> {
const params: CheckoutParams = {
- patron_id: this.context.patron.id()
+ patron_id: this.context.summary.id
};
if (this.checkoutNoncat) {
import {HoldNotifyUpdateDialogComponent} from './hold-notify-update.component';
import {BroadcastService} from '@eg/share/util/broadcast.service';
+const PATRON_FLESH_FIELDS = [
+ 'cards',
+ 'card',
+ 'groups',
+ 'standing_penalties',
+ 'settings',
+ 'addresses',
+ 'billing_address',
+ 'mailing_address',
+ 'stat_cat_entries',
+ 'waiver_entries',
+ 'usr_activity',
+ 'notes'
+];
+
const COMMON_USER_SETTING_TYPES = [
'circ.holds_behind_desk',
'circ.collections.exempt',
loadPatron(): Promise<any> {
if (this.patronId) {
- return this.patronService.getFleshedById(this.patronId)
+ return this.patronService.getFleshedById(this.patronId, PATRON_FLESH_FIELDS)
.then(patron => {
this.patron = patron;
this.origUsername = patron.usrname();
}
if (clone) {
- this.context.patron = null;
+ this.context.summary = null;
this.router.navigate(
['/staff/circ/patron/register/clone', this.modifiedPatron.id()]);
this.dataSource.getRows = (pager: Pager, sort: any[]) =>
from(this.patrons.slice(pager.offset, pager.offset + pager.limit));
- if (this.context.patron) {
- this.getGroupUsers(this.context.patron.usrgroup());
+ if (this.context.summary) {
+ this.getGroupUsers(this.context.summary.patron.usrgroup());
} else {
this.patronService.getById(this.patronId)
refresh() {
this.context.refreshPatron()
- .then(_ => this.usergroup = this.context.patron.usrgroup())
+ .then(_ => this.usergroup = this.context.summary.patron.usrgroup())
.then(_ => this.getGroupUsers(this.usergroup))
.then(_ => this.groupGrid.reload());
}
[showPlaceHoldButton]="true"
[hidePickupLibFilter]="true"
[defaultSort]="[{name:'request_time',dir:'asc'}]"
- [patronId]="context.patron.id()"></eg-holds-grid>
+ [patronId]="context.summary.patron.id()"></eg-holds-grid>
</ng-template>
</li>
[hidePickupLibFilter]="true"
[showRecentlyCanceled]="true"
[defaultSort]="[{name:'cancel_time',dir:'desc'}]"
- [patronId]="context.patron.id()"></eg-holds-grid>
+ [patronId]="context.summary.patron.id()"></eg-holds-grid>
</ng-template>
</li>
</ul>
-<ng-container *ngIf="context.patron">
+<ng-container *ngIf="context.summary">
<div [ngbNavOutlet]="holdsNav"></div>
</ng-container>
newHold() {
this.router.navigate(['/staff/catalog/search'],
- {queryParams: {holdForBarcode: this.context.patron.card().barcode()}});
+ {queryParams: {holdForBarcode: this.context.summary.patron.card().barcode()}});
}
}
</ng-container>
<li ngbNavItem="noncat">
<a ngbNavLink i18n>
- <ng-container *ngIf="context.patronStats">
- Non-Cataloged Circulations ({{context.patronStats.checkouts.noncat}})
+ <ng-container *ngIf="context.summary.stats">
+ Non-Cataloged Circulations ({{context.summary.stats.checkouts.noncat}})
</ng-container>
</a>
<ng-template ngbNavContent>
hideFields="id,usr,stop_date">
<eg-grid-toolbar-button i18n-label label="Apply Penalty / Message"
(onClick)="applyPenalty()">
+ <eg-grid-toolbar-action (onClick)="archive($event)"
+ label="Archive Selected" i18n-label></eg-grid-toolbar-action>
+ <eg-grid-toolbar-action (onClick)="remove($event)"
+ label="Delete Selected" i18n-label></eg-grid-toolbar-action>
+ <eg-grid-toolbar-action (onClick)="modify($event)"
+ label="Modify Selected" i18n-label></eg-grid-toolbar-action>
</eg-grid-toolbar-button>
<eg-grid-column path="standing_penalty.label">
</eg-grid-column>
import {Component, ViewChild, OnInit, Input, AfterViewInit} from '@angular/core';
-import {empty} from 'rxjs';
+import {empty, from} from 'rxjs';
+import {concatMap, tap} from 'rxjs/operators';
+import {IdlObject} from '@eg/core/idl.service';
import {NetService} from '@eg/core/net.service';
import {OrgService} from '@eg/core/org.service';
import {PcrudService} from '@eg/core/pcrud.service';
};
flesh.order_by = orderBy;
- return this.pcrud.search('ausp', query, flesh);
+ return this.pcrud.search('ausp', query, flesh, {authoritative: true});
}
this.archiveDataSource.getRows = (pager: Pager, sort: any[]) => {
flesh.order_by = orderBy;
- return this.pcrud.search('ausp', query, flesh);
+ return this.pcrud.search('ausp', query, flesh, {authoritative: true});
}
}
if (changes) { this.mainGrid.reload(); }
});
}
+
+ archive(penalties: IdlObject[]) {
+ penalties.forEach(p => p.stop_date('now'));
+ this.pcrud.update(penalties).toPromise()
+ .then(_ => {
+ this.mainGrid.reload();
+ this.archiveGrid.reload();
+ });
+ }
+
+ remove(penalties: IdlObject[]) {
+ this.pcrud.remove(penalties).toPromise()
+ .then(_ => {
+ this.mainGrid.reload();
+ this.archiveGrid.reload();
+ });
+ }
+
+ modify(penalties: IdlObject[]) {
+ let modified = false;
+ from(penalties).pipe(concatMap(penalty => {
+ this.penaltyDialog.penalty = penalty;
+ return this.penaltyDialog.open().pipe(tap(changed => {
+ if (changed) { modified = true; }
+ }));
+ })).toPromise().then(_ => {
+ if (modified) {
+ this.mainGrid.reload();
+ this.archiveGrid.reload();
+ }
+ });
+ }
}
-<ng-container *ngIf="!context.patron">
+<ng-container *ngIf="!context.summary">
<eg-staff-banner bannerText="Manage Patrons" i18n-bannerText>
</eg-staff-banner>
</ng-container>
-<ng-container *ngIf="context.patron">
+<ng-container *ngIf="context.summary">
<eg-staff-banner i18n-bannerText bannerText="Patron:
- {{context.patron.family_name()}},
- {{context.patron.first_given_name()}}
- {{context.patron.second_given_name()}}">
+ {{context.summary.patron.family_name()}},
+ {{context.summary.patron.first_given_name()}}
+ {{context.summary.patron.second_given_name()}}">
</eg-staff-banner>
</ng-container>
<ng-container *ngIf="showSummaryPane()">
<div class="col-lg-3">
<div class="sticky-top-with-nav bg-white">
- <ng-container *ngIf="context.patron">
- <eg-patron-summary [patron]="context.patron"
- [stats]="context.patronStats" [alerts]="context.alerts">
- </eg-patron-summary>
+ <ng-container *ngIf="context.summary">
+ <eg-patron-summary [summary]="context.summary"></eg-patron-summary>
</ng-container>
</div>
</div>
</li>
</ng-container>
- <li ngbNavItem="checkout" [disabled]="!context.patron">
+ <li ngbNavItem="checkout" [disabled]="!context.summary">
<a ngbNavLink i18n>Check Out</a>
<ng-template ngbNavContent>
<div class="">
</ng-template>
</li>
- <li ngbNavItem="items_out" [disabled]="!context.patron">
+ <li ngbNavItem="items_out" [disabled]="!context.summary">
<a ngbNavLink i18n>
Items Out ({{counts('checkouts', 'total_out')}})
</a>
</ng-template>
</li>
- <li ngbNavItem="holds" [disabled]="!context.patron">
+ <li ngbNavItem="holds" [disabled]="!context.summary">
<a ngbNavLink i18n>
Holds ({{counts('holds', 'ready')}} / {{counts('holds', 'total')}})
</a>
</ng-template>
</li>
- <li ngbNavItem="bills" [disabled]="!context.patron">
+ <li ngbNavItem="bills" [disabled]="!context.summary">
<a ngbNavLink (click)="billsTabClicked()" i18n>
Bills
<span [ngClass]="{'text-danger': counts('fines', 'balance_owed') > 0}">
</ng-template>
</li>
- <li ngbNavItem="messages" [disabled]="!context.patron">
+ <li ngbNavItem="messages" [disabled]="!context.summary">
<a ngbNavLink i18n>Messages</a>
<ng-template ngbNavContent>
<div class="">
</ng-template>
</li>
- <li ngbNavItem="edit" [disabled]="!context.patron">
+ <li ngbNavItem="edit" [disabled]="!context.summary">
<a ngbNavLink i18n>Edit</a>
<ng-template ngbNavContent>
<eg-patron-edit [patronId]="patronId" [toolbar]="editorToolbar">
</ng-template>
</li>
- <li ngbDropdown ngbNavItem="other" [disabled]="!context.patron">
- <a [attr.href]="context.patron ? '' : null"
+ <li ngbDropdown ngbNavItem="other" [disabled]="!context.summary">
+ <a [attr.href]="context.summary ? '' : null"
(click)="false" class="nav-link" ngbDropdownToggle>Other</a>
<div ngbDropdownMenu>
<a routerLink="/staff/circ/patron/{{patronId}}/alerts"
}
const prevId =
- this.context.patron ? this.context.patron.id() : null;
+ this.context.summary ? this.context.summary.id : null;
if (this.patronId) {
routeToAlertsPane() {
if (this.patronTab !== 'search' &&
- this.context.patron &&
- this.context.alerts.hasAlerts() &&
+ this.context.summary &&
+ this.context.summary.alerts.hasAlerts() &&
!this.context.patronAlertsShown()) {
this.router.navigate(['/staff/circ/patron', this.patronId, 'alerts']);
disablePurge(): boolean {
return (
- !this.context.patron ||
- this.context.patron.super_user() === 't' ||
+ !this.context.summary ||
+ this.context.summary.patron.super_user() === 't' ||
this.patronId === this.auth.user().id()
);
}
}
counts(part: string, field: string): number {
- if (this.context && this.context.patronStats) {
- return this.context.patronStats[part][field];
+ if (this.context.summary && this.context.summary.stats) {
+ return this.context.summary.stats[part][field];
} else {
return 0;
}
import {NetService} from '@eg/core/net.service';
import {OrgService} from '@eg/core/org.service';
import {AuthService} from '@eg/core/auth.service';
-import {PatronService, PatronStats, PatronAlerts
+import {PatronService, PatronSummary, PatronStats, PatronAlerts
} from '@eg/staff/share/patron/patron.service';
import {PatronSearch} from '@eg/staff/share/patron/search.component';
import {StoreService} from '@eg/core/store.service';
@Injectable()
export class PatronContextService {
- patron: IdlObject;
- patronStats: PatronStats;
- alerts: PatronAlerts;
-
+ summary: PatronSummary;
loaded = false;
-
lastPatronSearch: PatronSearch;
searchBarcode: string = null;
// Update the patron data without resetting all of the context data.
refreshPatron(id?: number): Promise<any> {
- if (!id) { id = this.patron.id(); }
- this.alerts = new PatronAlerts();
+ if (!id) {
+ if (!this.summary) {
+ return Promise.reject('no can do');
+ } else {
+ id = this.summary.id;
+ }
+ }
return this.patrons.getFleshedById(id, PATRON_FLESH_FIELDS)
- .then(p => this.patron = p)
+ .then(p => this.summary = new PatronSummary(p))
.then(_ => this.getPatronStats(id))
.then(_ => this.compileAlerts());
}
// 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.
- if (!this.patron) { return Promise.resolve(); }
+ if (!this.summary) { return Promise.resolve(); }
- return this.patrons.getVitalStats(this.patron)
- .then(stats => this.patronStats = stats);
+ return this.patrons.getVitalStats(this.summary.patron)
+ .then(stats => this.summary.stats = stats);
}
patronAlertsShown(): boolean {
- if (!this.patron) { return false; }
+ if (!this.summary) { 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());
+ if (shown === this.summary.patron.id()) { return true; }
+ this.store.setSessionItem('eg.circ.last_alerted_patron', this.summary.patron.id());
return false;
}
compileAlerts(): Promise<any> {
// User navigated to a different patron mid-data load.
- if (!this.patron) { return Promise.resolve(); }
+ if (!this.summary) { return Promise.resolve(); }
- return this.patrons.compileAlerts(this.patron, this.patronStats)
+ return this.patrons.compileAlerts(this.summary)
.then(alerts => {
- this.alerts = alerts;
+ this.summary.alerts = alerts;
if (this.searchBarcode) {
- const card = this.patron.cards()
+ const card = this.summary.patron.cards()
.filter(c => c.barcode() === this.searchBarcode)[0];
- this.alerts.retrievedWithInactive = card && card.active() === 'f';
+ this.summary.alerts.retrievedWithInactive =
+ card && card.active() === 'f';
this.searchBarcode = null;
}
});
<div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="lead" id="lead-record-1"
- [value]="context1.patron.id()" [(ngModel)]="leadAccount">
+ [value]="summary1.patron.id()" [(ngModel)]="leadAccount">
<label class="form-check-label" for="lead-record-1">Use as Lead?</label>
</div>
</div>
- <eg-patron-summary [patron]="context1.patron"
- [stats]="context1.stats" [alerts]="context1.alerts">
- </eg-patron-summary>
+ <eg-patron-summary [summary]="summary1"></eg-patron-summary>
</div>
<div class="col-lg-6">
<div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="lead" id="lead-record-2"
- [value]="context2.patron.id()" [(ngModel)]="leadAccount">
+ [value]="summary2.patron.id()" [(ngModel)]="leadAccount">
<label class="form-check-label" for="lead-record-2">Use as Lead?</label>
</div>
</div>
- <eg-patron-summary [patron]="context2.patron"
- [stats]="context2.stats" [alerts]="context2.alerts">
- </eg-patron-summary>
+ <eg-patron-summary [summary]="summary2"></eg-patron-summary>
</div>
</div>
</div>
import {EventService} from '@eg/core/event.service';
import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
import {DialogComponent} from '@eg/share/dialog/dialog.component';
-import {PatronService, PatronStats, PatronAlerts} from './patron.service';
+import {PatronService, PatronSummary} from './patron.service';
/**
+ * Dialog for merging 2 patron accounts.
*/
const PATRON_FLESH_FIELDS = [
'groups'
];
-class MergeContext {
- patron: IdlObject;
- stats: PatronStats;
- alerts: PatronAlerts;
-}
-
@Component({
selector: 'eg-patron-merge-dialog',
templateUrl: 'merge-dialog.component.html'
@Input() patronIds: [number, number];
- context1: MergeContext;
- context2: MergeContext;
+ summary1: PatronSummary;
+ summary2: PatronSummary;
leadAccount: number = null;
loading = true;
this.loading = true;
this.leadAccount = null;
this.loadPatron(this.patronIds[0])
- .then(ctx => this.context1 = ctx)
+ .then(ctx => this.summary1 = ctx)
.then(_ => this.loadPatron(this.patronIds[1]))
- .then(ctx => this.context2 = ctx)
+ .then(ctx => this.summary2 = ctx)
.then(_ => this.loading = false);
});
}
- loadPatron(id: number): Promise<MergeContext> {
- const ctx = new MergeContext();
+ loadPatron(id: number): Promise<PatronSummary> {
+ const sum = new PatronSummary();
return this.patrons.getFleshedById(id, PATRON_FLESH_FIELDS)
- .then(patron => ctx.patron = patron)
- .then(_ => this.patrons.getVitalStats(ctx.patron))
- .then(stats => ctx.stats = stats)
- .then(_ => this.patrons.compileAlerts(ctx.patron, ctx.stats))
- .then(alerts => ctx.alerts = alerts)
- .then(_ => ctx);
+ .then(patron => sum.patron = patron)
+ .then(_ => this.patrons.getVitalStats(sum.patron))
+ .then(stats => sum.stats = stats)
+ .then(_ => this.patrons.compileAlerts(sum))
+ .then(alerts => sum.alerts = alerts)
+ .then(_ => sum);
}
merge() {
import {BarcodeSelectComponent} from '@eg/staff/share/barcodes/barcode-select.component';
import {ServerStoreService} from '@eg/core/server-store.service';
-export interface PatronStats {
- fines: {
- balance_owed: number,
- group_balance_owed: number
+export class PatronStats {
+ fines = {
+ balance_owed: 0,
+ group_balance_owed: 0
};
- checkouts: {
- overdue: number,
- claims_returned: number,
- lost: number,
- out: number,
- total_out: number,
- long_overdue: number,
- noncat: number
+
+ checkouts = {
+ overdue: 0,
+ claims_returned: 0,
+ lost: 0,
+ out: 0,
+ total_out: 0,
+ long_overdue: 0,
+ noncat: 0
};
- holds: {
- ready: number;
- total: number;
+
+ holds = {
+ ready: 0,
+ total: 0
};
}
}
}
+export class PatronSummary {
+ id: number;
+ patron: IdlObject;
+ stats: PatronStats = new PatronStats();
+ alerts: PatronAlerts = new PatronAlerts();
+ constructor(patron?: IdlObject) {
+ if (patron) {
+ this.id = patron.id();
+ this.patron = patron;
+ }
+ }
+}
@Injectable()
export class PatronService {
});
}
- compileAlerts(patron: IdlObject, stats: PatronStats): Promise<PatronAlerts> {
+ compileAlerts(summary: PatronSummary): Promise<PatronAlerts> {
+ const patron = summary.patron;
+ const stats = summary.stats;
const alerts = new PatronAlerts();
alerts.holdsReady = stats.holds.ready;
SILENT_NOTE = 21;
STAFF_CHR = 25;
- staffInitials: string;
+ penalty: IdlObject; // modifying an existing penalty
penaltyTypes: IdlObject[];
penaltyTypeFromSelect = '';
- penaltyTypeFromButton;
+ penaltyTypeFromButton: number;
patron: IdlObject;
dataLoaded = false;
requireInitials = false;
init(): Observable<any> {
this.dataLoaded = false;
- this.penaltyTypeFromButton = this.SILENT_NOTE;
+ if (this.penalty) { // Modifying an existing penalty
+ const pen = this.penalty;
+ const sp = pen.standing_penalty().id();
+ if (sp === this.ALERT_NOTE ||
+ sp === this.SILENT_NOTE || sp === this.STAFF_CHR) {
+ this.penaltyTypeFromButton = sp;
+ } else {
+ this.penaltyTypeFromSelect = sp;
+ }
+
+ this.noteText = pen.note();
+
+ } else {
+ this.penaltyTypeFromButton = this.SILENT_NOTE;
+ }
this.org.settings(['ui.staff.require_initials.patron_standing_penalty'])
.then(sets => this.requireInitials =
}));
}
+ modifyPenalty() {
+ this.penalty.note(this.initials ?
+ `${this.noteText} [${this.initials}]` : this.noteText);
+
+ this.penalty.standing_penalty(
+ this.penaltyTypeFromSelect || this.penaltyTypeFromButton);
+
+ this.pcrud.update(this.penalty).toPromise()
+ .then(ok => {
+ if (!ok) {
+ this.errorMsg.current().then(msg => this.toast.danger(msg));
+ this.error('Update failed', true);
+ } else {
+ this.successMsg.current().then(msg => this.toast.success(msg));
+ this.penalty = null;
+ this.close(ok);
+ }
+ });
+ }
+
apply() {
+ if (this.penalty) {
+ this.modifyPenalty();
+ return;
+ }
+
const pen = this.idl.create('ausp');
const msg = {
title: this.title,
<div class="row d-flex">
<div class="flex-1 pt-1">
<h4 class="font-weight-bold" i18n>
- {{patron.family_name()}},
- {{patron.first_given_name()}}
- {{patron.second_given_name()}}
+ {{p().family_name()}},
+ {{p().first_given_name()}}
+ {{p().second_given_name()}}
</h4>
</div>
<ng-container *ngIf="hasPrefName()">
<div class="row d-flex border-top" *ngIf="hasPrefName()">
<div class="flex-1 pt-1">
<h4 class="font-weight-bold" i18n>
- {{patronService.namePart(patron, 'family_name')}},
- {{patronService.namePart(patron, 'first_given_name')}}
- {{patronService.namePart(patron, 'second_given_name')}}
+ {{patronService.namePart(p(), 'family_name')}},
+ {{patronService.namePart(p(), 'first_given_name')}}
+ {{patronService.namePart(p(), 'second_given_name')}}
</h4>
</div>
<div class="mr-2 ml-2 text-info font-italic" i18n>preferred</div>
</div>
<div class="row mb-1 alert alert-danger p-0"
- *ngIf="alerts.accountExpiresSoon">
+ *ngIf="summary.alerts.accountExpiresSoon">
<div class="col-lg-12" i18n>
Patron account will expire soon. Please renew.
</div>
</div>
<div class="row mb-1 alert alert-danger p-0"
- *ngFor="let pen of alerts.alertPenalties">
+ *ngFor="let pen of summary.alerts.alertPenalties">
<div class="col-lg-9"
title="{{pen.standing_penalty().name()}}">
{{pen.note() || pen.standing_penalty().label()}}
<div class="row mb-1">
<div class="col-lg-5" i18n>Profile</div>
- <div class="col-lg-7">{{patron.profile().name()}}</div>
+ <div class="col-lg-7">{{p().profile().name()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Home Library</div>
- <div class="col-lg-7">{{orgSn(patron.home_ou())}}</div>
+ <div class="col-lg-7">{{orgSn(p().home_ou())}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Net Access</div>
- <div class="col-lg-7">{{patron.net_access_level().name()}}</div>
+ <div class="col-lg-7">{{p().net_access_level().name()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Date of Birth</div>
- <div class="col-lg-7">{{patron.dob() | date:'shortDate'}}</div>
+ <div class="col-lg-7">{{p().dob() | date:'shortDate'}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Parent/Guardian</div>
- <div class="col-lg-7">{{patron.guardian()}}</div>
+ <div class="col-lg-7">{{p().guardian()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Last Activity</div>
<div class="col-lg-7">
- <ng-container *ngIf="patron.usr_activity()[0]">
- {{patron.usr_activity()[0].event_time() | date:'shortDate'}}
+ <ng-container *ngIf="p().usr_activity()[0]">
+ {{p().usr_activity()[0].event_time() | date:'shortDate'}}
</ng-container>
</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Last Updated</div>
- <div class="col-lg-7">{{patron.last_update_time() | date:'shortDate'}}</div>
+ <div class="col-lg-7">{{p().last_update_time() | date:'shortDate'}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Create Date</div>
- <div class="col-lg-7">{{patron.create_date() | date:'shortDate'}}</div>
+ <div class="col-lg-7">{{p().create_date() | date:'shortDate'}}</div>
</div>
- <div class="row" [ngClass]="{'alert alert-danger p-0': alerts.accountExpired}">
+ <div class="row" [ngClass]="{'alert alert-danger p-0': summary.alerts.accountExpired}">
<div class="col-lg-5" i18n>Expire Date</div>
- <div class="col-lg-7">{{patron.expire_date() | date:'shortDate'}}</div>
+ <div class="col-lg-7">{{p().expire_date() | date:'shortDate'}}</div>
</div>
<hr class="m-1"/>
- <ng-container *ngIf="stats">
+ <ng-container *ngIf="summary">
<div class="row mb-1"
- [ngClass]="{'alert alert-danger p-0': stats.fines.balance_owed > 0}">
+ [ngClass]="{'alert alert-danger p-0': summary.stats.fines.balance_owed > 0}">
<div class="col-lg-5" i18n>Fines Owed</div>
- <div class="col-lg-7">{{stats.fines.balance_owed | currency}}</div>
+ <div class="col-lg-7">{{summary.stats.fines.balance_owed | currency}}</div>
</div>
<ng-container
- *ngIf="stats.fines.group_balance_owed > stats.fines.balance_owed">
+ *ngIf="summary.stats.fines.group_balance_owed > summary.stats.fines.balance_owed">
<div class="row mb-1 alert alert-danger p-0">
<div class="col-lg-5" i18n>Group Fines</div>
- <div class="col-lg-7">{{stats.fines.group_balance_owed | currency}}</div>
+ <div class="col-lg-7">{{summary.stats.fines.group_balance_owed | currency}}</div>
</div>
</ng-container>
<div class="row mb-1">
<div class="col-lg-5" i18n>Items Out</div>
- <div class="col-lg-7">{{stats.checkouts.total_out}}</div>
+ <div class="col-lg-7">{{summary.stats.checkouts.total_out}}</div>
</div>
<div class="row mb-1"
- [ngClass]="{'alert alert-danger p-0': stats.checkouts.overdue > 0}">
+ [ngClass]="{'alert alert-danger p-0': summary.stats.checkouts.overdue > 0}">
<div class="col-lg-5" i18n>Overdue</div>
- <div class="col-lg-7">{{stats.checkouts.overdue}}</div>
+ <div class="col-lg-7">{{summary.stats.checkouts.overdue}}</div>
</div>
<div class="row mb-1"
- [ngClass]="{'alert alert-danger p-0': stats.checkouts.long_overdue > 0}">
+ [ngClass]="{'alert alert-danger p-0': summary.stats.checkouts.long_overdue > 0}">
<div class="col-lg-5" i18n>Long Overdue</div>
- <div class="col-lg-7">{{stats.checkouts.long_overdue}}</div>
+ <div class="col-lg-7">{{summary.stats.checkouts.long_overdue}}</div>
</div>
<div class="row mb-1"
- [ngClass]="{'alert alert-danger p-0': stats.checkouts.claims_returned > 0}">
+ [ngClass]="{'alert alert-danger p-0': summary.stats.checkouts.claims_returned > 0}">
<div class="col-lg-5" i18n>Claimed Returned</div>
- <div class="col-lg-7">{{stats.checkouts.claims_returned}}</div>
+ <div class="col-lg-7">{{summary.stats.checkouts.claims_returned}}</div>
</div>
<div class="row mb-1"
- [ngClass]="{'alert alert-danger p-0': stats.checkouts.lost > 0}">
+ [ngClass]="{'alert alert-danger p-0': summary.stats.checkouts.lost > 0}">
<div class="col-lg-5" i18n>Lost</div>
- <div class="col-lg-7">{{stats.checkouts.lost}}</div>
+ <div class="col-lg-7">{{summary.stats.checkouts.lost}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Non-Cataloged</div>
- <div class="col-lg-7">{{stats.checkouts.noncat}}</div>
+ <div class="col-lg-7">{{summary.stats.checkouts.noncat}}</div>
</div>
<div class="row">
<div class="col-lg-5" i18n>Holds</div>
<div class="col-lg-7">
- {{stats.holds.ready}} / {{stats.holds.total}}
+ {{summary.stats.holds.ready}} / {{summary.stats.holds.total}}
</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Card</div>
<div class="col-lg-7">
- {{patron.card() ? patron.card().barcode() : ''}}
+ {{p().card() ? p().card().barcode() : ''}}
</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Username</div>
- <div class="col-lg-7">{{patron.usrname()}}</div>
+ <div class="col-lg-7">{{p().usrname()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Day Phone</div>
- <div class="col-lg-7">{{patron.day_phone()}}</div>
+ <div class="col-lg-7">{{p().day_phone()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Evening Phone</div>
- <div class="col-lg-7">{{patron.evening_phone()}}</div>
+ <div class="col-lg-7">{{p().evening_phone()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Other Phone</div>
- <div class="col-lg-7">{{patron.other_phone()}}</div>
+ <div class="col-lg-7">{{p().other_phone()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>ID1 </div>
- <div class="col-lg-7">{{patron.ident_value()}}</div>
+ <div class="col-lg-7">{{p().ident_value()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>ID2</div>
- <div class="col-lg-7">{{patron.ident_value2()}}</div>
+ <div class="col-lg-7">{{p().ident_value2()}}</div>
</div>
<div class="row mb-1">
<div class="col-lg-5" i18n>Email</div>
<div class="col-lg-7">
<!-- TODO: mailto link -->
- {{patron.email()}}
+ {{p().email()}}
</div>
</div>
<hr class="m-1"/>
- <div class="row mb-1" *ngFor="let addr of patron.addresses()">
+ <div class="row mb-1" *ngFor="let addr of p().addresses()">
<div class="col-lg-12">
<fieldset>
<legend class="d-flex" [ngClass]="{'alert alert-danger p-0': addr.valid() == 'f'}">
<!-- hidden textare used only for copying the text -->
<textarea id="patron-address-copy-{{addr.id()}}" rows="2"
style="visibility:hidden">
-{{patron.first_given_name()}} {{patron.second_given_name()}} {{patron.family_name()}}
+{{p().first_given_name()}} {{p().second_given_name()}} {{p().family_name()}}
{{addr.street1()}} {{addr.street2()}}
{{addr.city()}}, {{addr.state()}} {{addr.post_code()}}</textarea>
</div>
import {IdlObject} from '@eg/core/idl.service';
import {NetService} from '@eg/core/net.service';
import {PrintService} from '@eg/share/print/print.service';
-import {PatronService, PatronStats, PatronAlerts} from './patron.service';
+import {PatronService, PatronSummary} from './patron.service';
@Component({
templateUrl: 'summary.component.html',
})
export class PatronSummaryComponent implements OnInit {
- @Input() patron: IdlObject;
- @Input() stats: PatronStats;
- @Input() alerts: PatronAlerts;
+ @Input() summary: PatronSummary;
constructor(
private org: OrgService,
ngOnInit() {
}
+ p(): IdlObject { // patron shorthand
+ return this.summary ? this.summary.patron : null;
+ }
+
hasPrefName(): boolean {
- if (this.patron) {
+ if (this.p()) {
return (
- this.patron.pref_first_given_name() ||
- this.patron.pref_second_given_name() ||
- this.patron.pref_family_name()
+ this.p().pref_first_given_name() ||
+ this.p().pref_second_given_name() ||
+ this.p().pref_family_name()
);
}
}
this.printer.print({
templateName: 'patron_address',
contextData: {
- patron: this.patron,
+ patron: this.p(),
address: addr
},
printContext: 'default'