<eg-progress-dialog #progressDialog></eg-progress-dialog>
<eg-barcode-select #barcodeSelect></eg-barcode-select>
<eg-copy-alerts-dialog #copyAlertsDialog></eg-copy-alerts-dialog>
+<eg-string #receiptEmailed i18n-text text="Receipt Successfully Emailed">
+</eg-string>
<eg-prompt-dialog #nonCatCount
promptType="number"
</div>
</div>
+<div class="row mt-3 pt-3">
+ <div class="col-lg-12 d-flex">
+ <div class="flex-1"></div>
+ <div class="mr-3">
+ <div class="form-check form-check-inline">
+ <input class="form-check-input" type="checkbox"
+ (ngModelChange)="toggleStrictBarcode($event)"
+ id="strict-barcode-cbox" [(ngModel)]="strictBarcode"/>
+ <label class="form-check-label"
+ for="strict-barcode-cbox" i18n>Strict Barcode</label>
+ </div>
+ </div>
+ <div class="mr-3">
+ <div class="input-group">
+ <button class="btn btn-outline-dark" (click)="quickReceipt()" i18n>
+ Quick Receipt
+ </button>
+ <div class="input-group-append">
+ <div ngbDropdown>
+ <button ngbDropdownToggle class="btn btn-outline-dark">
+ </button>
+ <div ngbDropdownMenu>
+ <button ngbDropdownItem (click)="emailReceipt()"
+ [disabled]="!mayEmailReceipt()" i18n>Email Receipt</button>
+ <button ngbDropdownItem (click)="printReceipt()" i18n>Print Receipt</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="mr-3">
+ <div class="input-group">
+ <button class="btn btn-outline-dark" (click)="doneAutoReceipt()" i18n>
+ Done
+ </button>
+ <div class="input-group-append">
+ <div ngbDropdown>
+ <button ngbDropdownToggle class="btn btn-outline-dark">
+ </button>
+ <div ngbDropdownMenu>
+ <button ngbDropdownItem (click)="emailReceipt(true)"
+ [disabled]="!mayEmailReceipt()" i18n>Email Receipt</button>
+ <button ngbDropdownItem
+ (click)="printReceipt(true)" i18n>Print Receipt</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
import {Component, OnInit, AfterViewInit, Input, ViewChild} from '@angular/core';
import {Router, ActivatedRoute, ParamMap} from '@angular/router';
-import {Observable, empty, of, from} from 'rxjs';
+import {Subscription, Observable, empty, of, from} from 'rxjs';
import {tap, switchMap} from 'rxjs/operators';
import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
import {IdlObject} from '@eg/core/idl.service';
} from '@eg/staff/share/holdings/copy-alerts-dialog.component';
import {BarcodeSelectComponent
} from '@eg/staff/share/barcodes/barcode-select.component';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {StringComponent} from '@eg/share/string/string.component';
+import {AuthService} from '@eg/core/auth.service';
+import {PrintService} from '@eg/share/print/print.service';
const SESSION_DUE_DATE = 'eg.circ.checkout.is_until_logout';
cellTextGenerator: GridCellTextGenerator;
dueDate: string;
dueDateOptions: 0 | 1 | 2 = 0; // auto date; specific date; session date
+ printOnComplete = true;
private copiesInFlight: {[barcode: string]: boolean} = {};
private copyAlertsDialog: CopyAlertsDialogComponent;
@ViewChild('barcodeSelect')
private barcodeSelect: BarcodeSelectComponent;
+ @ViewChild('receiptEmailed')
+ private receiptEmailed: StringComponent;
constructor(
+ private router: Router,
private store: StoreService,
private serverStore: ServerStoreService,
private org: OrgService,
public circ: CircService,
public patronService: PatronService,
public context: PatronContextService,
+ private toast: ToastService,
+ private auth: AuthService,
+ private printer: PrintService,
private audio: AudioService
) {}
this.dueDate = this.store.getSessionItem('eg.circ.checkout.due_date');
this.toggleDateOptions(2);
}
+
+ this.serverStore.getItem('circ.staff_client.do_not_auto_attempt_print')
+ .then(noPrint => {
+ this.printOnComplete = !(
+ noPrint &&
+ noPrint.includes('Checkout')
+ );
+ });
}
ngAfterViewInit() {
}
);
}
+
+ toggleStrictBarcode(active: boolean) {
+ if (active) {
+ this.serverStore.setItem('circ.checkout.strict_barcode', true);
+ } else {
+ this.serverStore.removeItem('circ.checkout.strict_barcode');
+ }
+ }
+
+ patronHasEmail(): boolean {
+ if (!this.context.summary) { return false; }
+ const patron = this.context.summary.patron;
+ return (
+ patron.email() &&
+ patron.email().match(/.*@.*/) !== null
+ );
+ }
+
+ mayEmailReceipt(): boolean {
+ if (!this.context.summary) { return false; }
+ const patron = this.context.summary.patron;
+ const setting = patron.settings()
+ .filter(s => s.name() === 'circ.send_email_checkout_receipts')[0];
+
+ return (
+ this.patronHasEmail() &&
+ setting &&
+ setting.value() === 'true' // JSON encoded
+ );
+ }
+
+ quickReceipt() {
+ if (this.mayEmailReceipt()) {
+ this.emailReceipt();
+ } else {
+ this.printReceipt();
+ }
+ }
+
+ doneAutoReceipt() {
+ if (this.mayEmailReceipt()) {
+ this.emailReceipt(true);
+ } else if (this.printOnComplete) {
+ this.printReceipt(true);
+ }
+ }
+
+ emailReceipt(redirect?: boolean) {
+ if (this.patronHasEmail() && this.context.checkouts.length > 0) {
+ return this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.checkout.batch_notify.session.atomic',
+ this.auth.token(),
+ this.context.summary.id,
+ this.context.checkouts.map(c => c.circ.id())
+ ).subscribe(_ => {
+ this.toast.success(this.receiptEmailed.text);
+ if (redirect) { this.doneRedirect(); }
+ });
+ }
+ }
+
+ printReceipt(redirect?: boolean) {
+ if (this.context.checkouts.length === 0) { return; }
+
+ if (redirect) {
+ // Wait for the print job to be queued before redirecting
+ const sub: Subscription =
+ this.printer.printJobQueued$.subscribe(_ => {
+ sub.unsubscribe();
+ this.doneRedirect();
+ });
+ }
+
+ this.printer.print({
+ printContext: 'default',
+ templateName: 'checkout',
+ contextData: {checkouts: this.context.checkouts}
+ });
+ }
+
+ doneRedirect() {
+ this.router.navigate(['/staff/circ/patron/bcsearch']);
+ }
+
}
toggleStrictBarcode(active: boolean) {
if (active) {
- this.store.setItem('circ.checkin.strict_barcode', true);
+ this.store.setItem('circ.renew.strict_barcode', true);
} else {
- this.store.removeItem('circ.checkin.strict_barcode');
+ this.store.removeItem('circ.renew.strict_barcode');
}
}
-<eg-string key="staff.holdings.copyalert.CHECKOUT.NORMAL"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.NORMAL"
text="Normal checkin" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST"
text="Item was marked lost" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST_AND_PAID"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST_AND_PAID"
text="Item was marked lost and paid for" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.MISSING"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.MISSING"
text="Item was marked missing" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.DAMAGED"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.DAMAGED"
text="Item was marked damaged" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSRETURNED"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSRETURNED"
text="Item was marked claims returned" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.LONGOVERDUE"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.LONGOVERDUE"
text="Item was marked long overdue" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSNEVERCHECKEDOUT"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSNEVERCHECKEDOUT"
text="Item was marked claims never checked out" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.NORMAL"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.NORMAL"
text="Normal checkout" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST"
text="Item was marked lost" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST_AND_PAID"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.LOST_AND_PAID"
text="Item was marked lost and paid for" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.MISSING"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.MISSING"
text="Item was marked missing" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.DAMAGED"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.DAMAGED"
text="Item was marked damaged" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSRETURNED"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSRETURNED"
text="Item was marked claims returned" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.LONGOVERDUE"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.LONGOVERDUE"
text="Item was marked long overdue" i18n-text></eg-string>
-<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSNEVERCHECKEDOUT"
+<eg-string key="staff.holdings.copyalert.CHECKOUT.CLAIMSNEVERCHECKEDOUT"
text="Item was marked claims never checked out" i18n-text></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.NORMAL"
+ i18n-text text="Normal checkout"></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.LOST"
+ i18n-text text="Item was marked lost"></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.LOST_AND_PAID"
+ i18n-text text="Item was marked lost and paid for"></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.MISSING"
+ i18n-text text="Item was marked missing"></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.DAMAGED"
+ i18n-text text="Item was marked damaged"></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.CLAIMSRETURNED"
+ i18n-text text="Item was marked claims returned"></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.LONGOVERDUE"
+ i18n-text text="Item was marked long overdue"></eg-string>
+<eg-string key="staff.holdings.copyalert.CHECKIN.CLAIMSNEVERCHECKEDOUT"
+ i18n-text text="Item was marked claims never checked out"></eg-string>
+
+
<ng-template #dialogContent>
<div class="modal-header bg-info">
<h4 class="modal-title" i18n>Item Alerts</h4>
{{alert._message}}
</div>
<div class="col-lg-2">
- <button class="btn btn-sm btn-outline-dark mr-2" *ngIf="canBeAcked(alert)"
+ <button class="btn btn-sm btn-outline-dark mr-2" *ngIf="canBeAcked(alert)"
(click)="alert._acked = !alert._acked" i18n>Clear</button>
</div>
</div>
- <div class="row border-top mt-3 pt-3"
+ <div class="row border-top mt-3 pt-3"
*ngIf="mode == 'checkin' && nextStatuses.length > 0; let index = index">
<div class="col-lg-4" i18n>Next item status:</div>
<div class="col-lg-5">
if (copyAlert.temp() === 'f') { return promise; }
- copyAlert.alert_type().next_status().forEach(statId => {
- if (!nextStatuses.includes(statId)) {
- nextStatuses.push(statId);
- }
- });
+ if (copyAlert.alert_type().next_status()) {
+ copyAlert.alert_type().next_status().forEach(statId => {
+ if (!nextStatuses.includes(statId)) {
+ nextStatuses.push(statId);
+ }
+ });
+ }
if (this.mode === 'checkin' && nextStatuses.length > 0) {
record(entry: WorkLogEntry) {
-console.log('1');
if (this.maxEntries === null) {
throw new Error('WorkLogService.loadSettings() required');
return;
'Add <eg-worklog-strings-components/> to your component for worklog support');
return;
}
-console.log('1');
entry.when = new Date();
entry.actor = this.auth.user().usrname();
- console.log(`worklog_${entry.action}`);
- console.log(this.workLogStrings[`worklog_${entry.action}`]);
entry.msg = this.workLogStrings[`worklog_${entry.action}`].text;
-console.log('1');
const workLog: WorkLogEntry[] =
this.store.getLocalItem('eg.work_log') || [];
let patronLog: WorkLogEntry[] =
this.store.getLocalItem('eg.patron_log') || [];
-console.log('1');
workLog.push(entry);
if (workLog.length > this.maxEntries) {
workLog.shift();
}
-console.log('1');
this.store.setLocalItem('eg.work_log', workLog);
-console.log('1');
if (entry.patron_id) {
// Remove existing entries that match this patron
patronLog = patronLog.filter(e => e.patron_id !== entry.patron_id);
-console.log('1');
patronLog.push(entry);
if (patronLog.length > this.maxPatrons) {
BEGIN;
+/*
+
-- SELECT evergreen.upgrade_deps_block_check('TODO', :eg_version);
-- insert then update for easier iterative development tweaks
</div>
$TEMPLATE$ WHERE name = 'bills_historical';
+*/
+
+INSERT INTO config.print_template
+ (name, label, owner, active, locale, content_type, template)
+VALUES ('checkout', 'Checkout', 1, TRUE, 'en-US', 'text/html', '');
+
+UPDATE config.print_template SET template = $TEMPLATE$
+[%
+ USE date;
+ USE money = format('$%.2f');
+ SET checkouts = template_data.checkouts;
+%]
+
+<div>
+ <div>Welcome to [% staff_org.name %]</div>
+ <div>You checked out the following items:</div>
+ <hr/>
+ <ol>
+ [% FOR checkout IN checkouts %]
+ <li>
+ <div>[% checkout.title %]</div>
+ <span>Barcode: </span>
+ <span>[% checkout.copy.barcode %]</span>
+ <span>Call Number: </span>
+ <span>
+ [% IF checkout.volume %]
+ [% volume.prefix.label %] [% volume.label %] [% volume.suffix.label %]
+ [% ELSE %]
+ Not Cataloged
+ [% END %]
+ </span>
+ </li>
+ [% END %]
+ </ol>
+ <hr/>
+ <div>Slip Date: [% date.format(date.now, '%x %r') %]</div>
+ <div>Printed by [% staff.first_given_name %] at [% staff_org.shortname %]</div>
+</div>
+
+$TEMPLATE$ WHERE name = 'checkout';
+
+INSERT INTO config.print_template
+ (name, label, owner, active, locale, content_type, template)
+VALUES ('renew', 'renew', 1, TRUE, 'en-US', 'text/html', '');
+
+UPDATE config.print_template SET template = $TEMPLATE$
+[%
+ USE date;
+ USE money = format('$%.2f');
+ SET renewals = template_data.renewals;
+%]
+
+<div>
+ <div>Welcome to [% staff_org.name %]</div>
+ <div>You renewed the following items:</div>
+ <hr/>
+ <ol>
+ [% FOR renewal IN renewals %]
+ <li>
+ <div>[% renewal.title %]</div>
+ <span>Barcode: </span>
+ <span>[% renewal.copy.barcode %]</span>
+ <span>Call Number: </span>
+ <span>
+ [% IF renewal.volume %]
+ [% volume.prefix.label %] [% volume.label %] [% volume.suffix.label %]
+ [% ELSE %]
+ Not Cataloged
+ [% END %]
+ </span>
+ </li>
+ [% END %]
+ </ol>
+ <hr/>
+ <div>Slip Date: [% date.format(date.now, '%x %r') %]</div>
+ <div>Printed by [% staff.first_given_name %] at [% staff_org.shortname %]</div>
+</div>
+
+$TEMPLATE$ WHERE name = 'renew';
+
COMMIT;