--- /dev/null
+<eg-prompt-dialog #moveToGroupDialog
+ i18n-dialogBody dialogBody="Enter the patron barcode">
+</eg-prompt-dialog>
+<eg-alert-dialog #userNotFoundDialog
+ i18n-dialogBody dialogBody="User not found">
+</eg-alert-dialog>
+
+<h3 i18n>Group Member Details</h3>
+
+<div class="row">
+ <div class="col-lg-12">
+ <span i18n>Total Owed:</span>
+ <span class="ml-1">{{totalOwed | currency}}</span>
+ <span class="ml-2">Total Out:</span>
+ <span class="ml-1">{{totalOut}}</span>
+ <span class="ml-2">Total Overdue:</span>
+ <span class="ml-1">{{totalOverdue}}</span>
+ </div>
+</div>
+
+
+<div class="mt-4">
+
+ <eg-grid idlClass="au" #groupGrid
+ [dataSource]="dataSource" [sortable]="true" [useLocalSort]="true"
+ showFields="active,barred,dob,family_name,first_given_name,
+ master_account,second_given_name,balance_owed,total_out,overdue">
+
+ <eg-grid-toolbar-button label="Move Another Patron To This Group"
+ i18n-label (onClick)="movePatronToGroup()">
+ </eg-grid-toolbar-button>
+
+ <eg-grid-column name="balance_owed" path="_stats.fines.balance_owed"
+ datatype="currency" label="Balance Owed" i18n-label>
+ </eg-grid-column>
+ <eg-grid-column name="total_out" path="_stats.checkouts.total_out"
+ label="Items Out" i18n-label>
+ </eg-grid-column>
+ <eg-grid-column name="overdue" path="_stats.checkouts.overdue"
+ label="Items Overdue" i18n-label>
+ </eg-grid-column>
+
+ </eg-grid>
+
+</div>
+
--- /dev/null
+import {Component, Input, OnInit, AfterViewInit, ViewChild} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {from, empty, range} from 'rxjs';
+import {concatMap, tap, takeLast} from 'rxjs/operators';
+import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+import {IdlObject} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {OrgService} from '@eg/core/org.service';
+import {NetService} from '@eg/core/net.service';
+import {PcrudService, PcrudContext} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {PatronService} from '@eg/staff/share/patron/patron.service';
+import {PatronContextService} from './patron.service';
+import {GridDataSource, GridColumn, GridCellTextGenerator} from '@eg/share/grid/grid';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {Pager} from '@eg/share/util/pager';
+import {PromptDialogComponent} from '@eg/share/dialog/prompt.component';
+import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
+import {ConfirmDialogComponent} from '@eg/share/dialog/confirm.component';
+
+@Component({
+ templateUrl: 'group.component.html',
+ selector: 'eg-patron-group'
+})
+export class PatronGroupComponent implements OnInit {
+
+ @Input() patronId: number;
+ patrons: IdlObject[] = [];
+ totalOwed = 0;
+ totalOut = 0;
+ totalOverdue = 0;
+ usergroup: number;
+
+ dataSource: GridDataSource = new GridDataSource();
+ @ViewChild('groupGrid') private groupGrid: GridComponent;
+ @ViewChild('moveToGroupDialog') private moveToGroupDialog: PromptDialogComponent;
+ @ViewChild('userNotFoundDialog') private userNotFoundDialog: AlertDialogComponent;
+
+ constructor(
+ private router: Router,
+ private evt: EventService,
+ private net: NetService,
+ private auth: AuthService,
+ private org: OrgService,
+ private pcrud: PcrudService,
+ private patronService: PatronService,
+ private context: PatronContextService
+ ) {}
+
+ ngOnInit() {
+
+ 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());
+
+ } else {
+ this.patronService.getById(this.patronId)
+ .then(patron => this.getGroupUsers(patron.usrgroup()));
+ }
+ }
+
+ getGroupUsers(usergroup: number) {
+ this.usergroup = usergroup;
+ this.patrons = [];
+
+ this.pcrud.search('au',
+ {usrgroup: usergroup, deleted: 'f'}, {authoritative: true})
+ .pipe(concatMap(u => {
+
+ const promise = this.context.getPatronVitalStats(u.id())
+ .then(stats => {
+ this.totalOwed += stats.fines.balance_owed;
+ this.totalOut += stats.checkouts.total_out;
+ this.totalOverdue += stats.checkouts.overdue;
+ u._stats = stats;
+ this.patrons.push(u);
+ });
+
+ return from(promise);
+
+ })).subscribe(null, null, () => this.groupGrid.reload());
+ }
+
+ movePatronToGroup() {
+
+ this.moveToGroupDialog.open().subscribe(barcode => {
+ if (!barcode) { return null; }
+
+ this.patronService.getByBarcode(barcode)
+ .then(resp => {
+ if (resp === null) {
+ this.userNotFoundDialog.open();
+ return null;
+ }
+
+ resp.usrgroup(this.usergroup);
+ resp.ischanged(true);
+
+ return this.net.request(
+ 'open-ils.actor',
+ 'open-ils.actor.patron.update',
+ this.auth.token(), resp
+ ).toPromise();
+ })
+ .then(resp => {
+ if (resp === null) { return null; }
+
+ const evt = this.evt.parse(resp);
+ if (evt) {
+ console.error(evt);
+ alert(evt);
+ return null;
+ }
+
+ return this.getGroupUsers(this.usergroup);
+ })
+ .then(resp => {
+ if (resp === null) { return null; }
+ this.groupGrid.reload();
+ });
+ });
+ }
+}
.then(_ => this.compileAlerts());
}
- getPatronStats(id: number): Promise<any> {
-
- // 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(); }
+ getPatronVitalStats(id: number): Promise<PatronStats> {
return this.net.request(
'open-ils.actor',
stats.checkouts.total_out += stats.checkouts.lost;
}
- this.patronStats = stats;
+ return stats;
+ });
+ }
+
+ getPatronStats(id: number): Promise<any> {
+
+ // 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(); }
+
+ return this.getPatronVitalStats(id)
+
+ .then(stats => this.patronStats = stats)
- }).then(_ => {
+ .then(_ => {
if (!this.patron) { return; }