<eg-progress-dialog #progressDialog></eg-progress-dialog>
<eg-barcode-select #barcodeSelect></eg-barcode-select>
<eg-copy-alerts-dialog #copyAlertsDialog></eg-copy-alerts-dialog>
+<eg-bucket-dialog #bucketDialog></eg-bucket-dialog>
+<eg-string #itemNeverCircedStr i18n-text
+ text="Item CONC40000598 has never circulated."></eg-string>
+<eg-backdate-dialog #backdateDialog></eg-backdate-dialog>
<div class="row" *ngIf="hasAlerts()">
<div class="col-lg-12 alert alert-danger p-1">
[useLocalSort]="true" [cellTextGenerator]="cellTextGenerator"
[disablePaging]="true" persistKey="circ.checkin">
- <!--
- <eg-grid-toolbar-action
- i18n-group group="Add" i18n-label label="Add Item Alerts"
- (onClick)="openItemAlerts($event, 'create')">
- </eg-grid-toolbar-action>
-
- <eg-grid-toolbar-action
- i18n-group group="Add" i18n-label label="Manage Item Alerts"
- [disabled]="checkinsGrid.context.rowSelector.selected().length !== 1"
- (onClick)="openItemAlerts($event, 'manage')">
- </eg-grid-toolbar-action>
- -->
+ <eg-grid-toolbar-action
+ group="Mark" i18n-group i18n-label label="Mark Item Damaged"
+ (onClick)="markDamaged($event)"></eg-grid-toolbar-action>
+
+ <eg-grid-toolbar-action
+ i18n-group group="Edit" i18n-label label="Manage Item Alerts"
+ [disabled]="grid.context.rowSelector.selected().length !== 1"
+ (onClick)="manageItemAlerts($event)">
+ </eg-grid-toolbar-action>
+
+ <eg-grid-toolbar-action
+ i18n-group group="Add" i18n-label label="Add Item Alerts"
+ (onClick)="addItemAlerts($event)">
+ </eg-grid-toolbar-action>
+
+ <eg-grid-toolbar-action
+ i18n-group group="Add" i18n-label label="Add Items To Bucket"
+ (onClick)="openBucketDialog($event)">
+ </eg-grid-toolbar-action>
+
+ <eg-grid-toolbar-action
+ i18n-group group="Edit" i18n-label label="Backdate Post-Checkin"
+ (onClick)="backdatePostCheckin($event)">
+ </eg-grid-toolbar-action>
+
+ <eg-grid-toolbar-action
+ i18n-group group="Retrieve" i18n-label
+ label="Retrieve Last Patron Who Circulated Item"
+ [disabled]="grid.context.rowSelector.selected().length !== 1"
+ (onClick)="retrieveLastPatron($event)">
+ </eg-grid-toolbar-action>
+
+ <!-- COLUMNS -->
<eg-grid-column path="index" [index]="true"
label="Row Index" i18n-label [hidden]="true"></eg-grid-column>
import {Component, ViewChild, OnInit, AfterViewInit, HostListener} from '@angular/core';
import {Router, ActivatedRoute, ParamMap} from '@angular/router';
import {from} from 'rxjs';
+import {concatMap} from 'rxjs/operators';
+import {IdlObject, IdlService} 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 {GridDataSource, GridColumn, GridCellTextGenerator} from '@eg/share/grid/grid';
import {GridComponent} from '@eg/share/grid/grid.component';
+import {Pager} from '@eg/share/util/pager';
import {CircService, CircDisplayInfo, CheckinParams, CheckinResult
} from '@eg/staff/share/circ/circ.service';
-import {Pager} from '@eg/share/util/pager';
import {BarcodeSelectComponent
} from '@eg/staff/share/barcodes/barcode-select.component';
import {PrintService} from '@eg/share/print/print.service';
+import {MarkDamagedDialogComponent
+ } from '@eg/staff/share/holdings/mark-damaged-dialog.component';
+import {CopyAlertsDialogComponent
+ } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
+import {BucketDialogComponent
+ } from '@eg/staff/share/buckets/bucket-dialog.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {BackdateDialogComponent} from '@eg/staff/share/circ/backdate-dialog.component';
interface CheckinGridEntry extends CheckinResult {
// May need to extend...
isHoldCapture = false;
strictBarcode = false;
trimList = false;
+ itemNeverCirced: string;
gridDataSource: GridDataSource = new GridDataSource();
cellTextGenerator: GridCellTextGenerator;
@ViewChild('grid') private grid: GridComponent;
@ViewChild('barcodeSelect') private barcodeSelect: BarcodeSelectComponent;
+ @ViewChild('markDamagedDialog') private markDamagedDialog: MarkDamagedDialogComponent;
+ @ViewChild('copyAlertsDialog') private copyAlertsDialog: CopyAlertsDialogComponent;
+ @ViewChild('bucketDialog') private bucketDialog: BucketDialogComponent;
+ @ViewChild('itemNeverCircedStr') private itemNeverCircedStr: StringComponent;
+ @ViewChild('backdateDialog') private backdateDialog: BackdateDialogComponent;
constructor(
private router: Router,
private auth: AuthService,
private store: ServerStoreService,
private circ: CircService,
+ private toast: ToastService,
private printer: PrintService,
public patronService: PatronService
) {}
this.modifiers.auto_print_holds_transits
);
}
+
+ getCopyIds(rows: CheckinGridEntry[], skipStatus?: number): number[] {
+ return this.getCopies(rows, skipStatus).map(c => Number(c.id()));
+ }
+
+ getCopies(rows: CheckinGridEntry[], skipStatus?: number): IdlObject[] {
+ let copies = rows.filter(r => r.copy).map(r => r.copy);
+ if (skipStatus) {
+ copies = copies.filter(
+ c => Number(c.status().id()) !== Number(skipStatus));
+ }
+ return copies;
+ }
+
+
+ markDamaged(rows: CheckinGridEntry[]) {
+ const copyIds = this.getCopyIds(rows, 14 /* ignore damaged */);
+ if (copyIds.length === 0) { return; }
+
+ from(copyIds).pipe(concatMap(id => {
+ this.markDamagedDialog.copyId = id;
+ return this.markDamagedDialog.open({size: 'lg'});
+ }));
+ }
+
+ addItemAlerts(rows: CheckinGridEntry[]) {
+ const copyIds = this.getCopyIds(rows);
+ if (copyIds.length === 0) { return; }
+
+ this.copyAlertsDialog.copyIds = copyIds;
+ this.copyAlertsDialog.mode = 'create'
+ this.copyAlertsDialog.open({size: 'lg'}).subscribe();
+ }
+
+ manageItemAlerts(rows: CheckinGridEntry[]) {
+ const copyIds = this.getCopyIds(rows);
+ if (copyIds.length === 0) { return; }
+
+ this.copyAlertsDialog.copyIds = copyIds;
+ this.copyAlertsDialog.mode = 'manage';
+ this.copyAlertsDialog.open({size: 'lg'}).subscribe();
+ }
+
+ openBucketDialog(rows: CheckinGridEntry[]) {
+ const copyIds = this.getCopyIds(rows);
+ if (copyIds.length > 0) {
+ this.bucketDialog.bucketClass = 'copy';
+ this.bucketDialog.itemIds = copyIds;
+ this.bucketDialog.open({size: 'lg'});
+ }
+ }
+
+ retrieveLastPatron(rows: CheckinGridEntry[]) {
+ const copy = this.getCopies(rows).pop();
+ if (!copy) { return; }
+
+ this.circ.lastCopyCirc(copy.id()).then(circ => {
+ if (circ) {
+ this.router.navigate(['/staff/circ/patron', circ.usr(), 'checkout']);
+ } else {
+ this.itemNeverCirced = copy.barcode();
+ setTimeout(() => this.toast.danger(this.itemNeverCircedStr.text));
+ }
+ });
+ }
+
+ backdatePostCheckin(rows: CheckinGridEntry[]) {
+ const circs = rows.map(r => r.circ).filter(circ => Boolean(circ));
+ if (circs.length === 0) { return; }
+
+ this.backdateDialog.circIds = circs.map(c => c.id());
+ this.backdateDialog.open().subscribe(backdate => {
+ if (backdate) {
+ circs.forEach(circ => circ.checkin_time(backdate));
+ }
+ });
+ }
}
--- /dev/null
+
+<eg-string #successMsg text="Successfully Marked Claims Returned" i18n-text></eg-string>
+<eg-string #errorMsg text="Failed To Mark Claims Returned" i18n-text></eg-string>
+
+<ng-template #dialogContent>
+ <div class="modal-header bg-info">
+ <h4 class="modal-title">
+ <span i18n>Backdate Already Checked-In Circulations
+ </span>
+ </h4>
+ <button type="button" class="close"
+ i18n-aria-label aria-label="Close" (click)="close()">
+ <span aria-hidden="true">×</span>
+ </button>
+ </div>
+ <div class="modal-body">
+ <h5 i18n>Backdating {{circIds.length}} circulaion(s)</h5>
+ <div class="row mt-3">
+ <div class="col-lg-5" i18n>Effective Date:</div>
+ <div class="col-lg-7">
+ <eg-date-select [required]="true" [initialIso]="backdate"
+ (onChangeAsIso)="backdate = $event">
+ </eg-date-select>
+ </div>
+ </div>
+ <div class="row" *ngIf="updateCount > 0">
+ <div class="col-lg-6 offset-lg-3">
+ <span i18n>Updated {{updateCount}} of {{circIds.length}}...</span>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-success"
+ [disabled]="!backdate" (click)="modifyBatch()" i18n>Submit</button>
+ <button type="button" class="btn btn-warning"
+ (click)="close()" i18n>Cancel</button>
+ </div>
+</ng-template>
--- /dev/null
+import {Component, OnInit, Output, Input, ViewChild, EventEmitter} from '@angular/core';
+import {Observable, empty, of, from, throwError} from 'rxjs';
+import {concatMap, mergeMap, map} from 'rxjs/operators';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {EventService} from '@eg/core/event.service';
+
+@Component({
+ templateUrl: 'backdate-dialog.component.html',
+ selector: 'eg-backdate-dialog'
+})
+export class BackdateDialogComponent extends DialogComponent implements OnInit {
+
+ circIds: number[];
+ backdate: string;
+ updateCount = 0;
+
+ constructor(
+ private modal: NgbModal,
+ private net: NetService,
+ private auth: AuthService,
+ private evt: EventService
+ ) { super(modal); }
+
+ ngOnInit() {
+ this.onOpen$.subscribe(_ => {
+ this.updateCount = 0;
+ this.backdate = new Date().toISOString();
+ });
+ }
+
+ modifyBatch() {
+ this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.post_checkin_backdate.batch',
+ this.auth.token(), this.circIds, this.backdate
+ ).subscribe(
+ res => this.updateCount++,
+ err => console.error(err),
+ () => this.close(this.backdate)
+ );
+ }
+}
+
+
import {OpenCircDialogComponent} from './open-circ-dialog.component';
import {RouteDialogComponent} from './route-dialog.component';
import {CopyInTransitDialogComponent} from './in-transit-dialog.component';
+import {BackdateDialogComponent} from './backdate-dialog.component';
@NgModule({
declarations: [
ClaimsReturnedDialogComponent,
CircEventsComponent,
RouteDialogComponent,
+ BackdateDialogComponent,
CopyInTransitDialogComponent,
OpenCircDialogComponent
],
],
exports: [
CircGridComponent,
+ BackdateDialogComponent,
CircComponentsComponent
],
providers: [
return Promise.resolve();
});
}
+
+ lastCopyCirc(copyId: number): Promise<IdlObject> {
+ return this.pcrud.search('circ',
+ {target_copy : copyId},
+ {order_by : {circ : 'xact_start desc' }, limit : 1}
+ ).toPromise();
+ }
}