import {BrowsePagerComponent} from './result/browse-pager.component';
import {HttpClientModule} from '@angular/common/http';
import {BarcodesModule} from '@eg/staff/share/barcodes/barcodes.module';
+import {WorkLogModule} from '@eg/staff/share/worklog/worklog.module';
@NgModule({
declarations: [
PatronModule,
MarcEditModule,
HttpClientModule
- BarcodesModule
+ BarcodesModule,
+ WorkLogModule
],
providers: [
StaffCatalogService
<eg-patron-search-dialog #patronSearch></eg-patron-search-dialog>
<eg-barcode-select #barcodeSelect></eg-barcode-select>
+<eg-worklog-strings-components></eg-worklog-strings-components>
<eg-alert-dialog #activeDateAlert
i18n-dialogTitle i18n-dialogBody
} from '@eg/staff/share/patron/search-dialog.component';
import {BarcodeSelectComponent
} from '@eg/staff/share/barcodes/barcode-select.component';
+import {WorkLogService} from '@eg/staff/share/worklog/worklog.service';
class HoldContext {
holdMeta: HoldRequestTarget;
private staffCat: StaffCatalogService,
private holds: HoldsService,
private patron: PatronService,
- private perm: PermService
+ private perm: PermService,
+ private worklog: WorkLogService
) {
this.holdContexts = [];
this.smsCarriers = [];
settings['circ.staff_placed_holds_fallback_to_ws_ou'] === true;
this.puLibWsDefault =
settings['circ.staff_placed_holds_default_to_ws_ou'] === true;
- });
+ }).then(_ => this.worklog.loadSettings());
this.org.list().forEach(org => {
if (org.ou_type().can_have_vols() === 'f') {
if (request.result.success) {
ctx.success = true;
+ this.worklog.record({
+ action: 'requested_hold',
+ hold_id: request.result.holdId,
+ patron_id: this.user.id(),
+ user: this.user.family_name()
+ });
+
// Overrides are processed one hold at a time, so
// we have to invoke the post-holds logic here
// instead of the batch placeHolds() method. If
import {AudioService} from '@eg/share/util/audio.service';
import {ToastService} from '@eg/share/toast/toast.service';
import {GridFlatDataService} from '@eg/share/grid/grid-flat-data.service';
+import {WorkLogService} from '@eg/staff/share/worklog/worklog.service';
@Component({
templateUrl: 'bills.component.html',
private circ: CircService,
private billing: BillingService,
private flatData: GridFlatDataService,
+ private worklog: WorkLogService,
public patronService: PatronService,
public context: PatronContextService
) {}
);
})
.then(resp => {
+ this.worklog.record({
+ user: this.patron().family_name(),
+ patron_id: this.patron().id(),
+ amount: this.pendingPayment(),
+ action: 'paid_bill'
+ });
this.patron().last_xact_id(resp.last_xact_id);
return this.handlePayReceipt(payments, resp.payments);
})
collectParams(): Promise<CheckoutParams> {
const params: CheckoutParams = {
- patron_id: this.context.summary.id
+ patron_id: this.context.summary.id,
+ _worklog: {
+ user: this.context.summary.patron.family_name(),
+ patron_id: this.context.summary.id
+ }
};
if (this.checkoutNoncat) {
import {NetService} from '@eg/core/net.service';
import {AuthService} from '@eg/core/auth.service';
import {PatronService} from '@eg/staff/share/patron/patron.service';
-import {PatronContextService} from './patron.service';
import {PatronSearchFieldSet} from '@eg/staff/share/patron/search.component';
export enum VisibilityLevel {
private idl: IdlService,
private net: NetService,
private auth: AuthService,
- private patronService: PatronService,
- public context: PatronContextService
+ private patronService: PatronService
) {}
ngOnInit() {
});
}
- checkAddressAlerts(addr: IdlObject) {
+ checkAddressAlerts(patron: IdlObject, addr: IdlObject) {
const addrHash = this.idl.toHash(addr);
- const patron = this.context.summary.patron;
+ console.log('CHECKING ADDR', addrHash);
addrHash.mailing_address = addr.id() === patron.mailing_address().id();
addrHash.billing_address = addr.id() === patron.billing_address().id();
this.net.request(
import {HoldNotifyUpdateDialogComponent} from './hold-notify-update.component';
import {BroadcastService} from '@eg/share/util/broadcast.service';
import {PrintService} from '@eg/share/print/print.service';
+import {WorkLogService} from '@eg/staff/share/worklog/worklog.service';
const PATRON_FLESH_FIELDS = [
'cards',
private broadcaster: BroadcastService,
private patronService: PatronService,
private printer: PrintService,
+ private worklog: WorkLogService,
public context: PatronContextService
) {}
case 'city':
// dupe search on address wants the address object as the value.
this.dupeValueChange('address', obj);
- this.toolbar.checkAddressAlerts(obj);
+ this.toolbar.checkAddressAlerts(this.patron, obj);
break;
case 'post_code':
postSaveRedirect(clone: boolean) {
+ this.worklog.record({
+ user: this.modifiedPatron.family_name(),
+ patron_id: this.modifiedPatron.id(),
+ action: this.patron.isnew() ? 'registered_patron' : 'edited_patron'
+ });
+
if (this.stageUser) {
this.broadcaster.broadcast('eg.pending_usr.update',
{usr: this.idl.toHash(this.modifiedPatron)});
</ng-container>
<eg-circ-components></eg-circ-components>
+<eg-worklog-strings-components></eg-worklog-strings-components>
<div class="row">
import {PatronMessagesComponent} from './messages.component';
import {PatronPermsComponent} from './perms.component';
import {BillingHistoryComponent} from './billing-history.component';
+import {WorkLogModule} from '@eg/staff/share/worklog/worklog.module';
@NgModule({
declarations: [
BookingModule,
PatronModule,
PatronRoutingModule,
- BarcodesModule
+ BarcodesModule,
+ WorkLogModule
],
providers: [
PatronResolver,
<eg-staff-banner i18n-bannerText bannerText="Register New Patron">
</eg-staff-banner>
+<eg-worklog-strings-components></eg-worklog-strings-components>
+
<div class="sticky-top-with-nav bg-white">
<eg-patron-edit-toolbar #editorToolbar></eg-patron-edit-toolbar>
</div>
return Promise.reject(evt);
}
- // TODO work log
return response;
});
}
import {CopyInTransitDialogComponent} from './in-transit-dialog.component';
import {CancelTransitDialogComponent} from './cancel-transit-dialog.component';
import {BackdateDialogComponent} from './backdate-dialog.component';
-import {WorkLogService} from './work-log.service';
+import {WorkLogModule} from '@eg/staff/share/worklog/worklog.module';
@NgModule({
declarations: [
imports: [
StaffCommonModule,
HoldingsModule,
- BillingModule
+ BillingModule,
+ WorkLogModule
],
exports: [
CircGridComponent,
CircComponentsComponent
],
providers: [
- CircService,
- WorkLogService
+ CircService
]
})
import {StringService} from '@eg/share/string/string.service';
import {ServerStoreService} from '@eg/core/server-store.service';
import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
-import {WorkLogService, WorkLogEntry} from './work-log.service';
+import {WorkLogService, WorkLogEntry} from '@eg/staff/share/worklog/worklog.service';
export interface CircDisplayInfo {
title?: string;
// internal tracking
_override?: boolean;
_renewal?: boolean;
+ _worklog?: WorkLogEntry;
}
export interface CircResultCommon {
// internal / local values that are moved from the API request.
_override?: boolean;
+ _worklog?: WorkLogEntry;
}
export interface CheckinResult extends CircResultCommon {
result.nonCatCirc = payload.noncat_circ;
return this.fleshCommonData(result).then(_ => {
- this.addWorkLog(params._renewal ? 'renewal' : 'checkout', result);
+ const action = params._renewal ? 'renewal' :
+ (params.noncat ? 'noncat_checkout' : 'checkout');
+ this.addWorkLog(action, result);
return result;
});
}
}
addWorkLog(action: string, result: CircResultCommon) {
- const entry: WorkLogEntry = {action: action};
-
const params = result.params;
const copy = result.copy;
const patron = result.patron;
+ // Some worklog data may be provided by the caller in the params.
+ const entry: WorkLogEntry =
+ Object.assign(params._worklog || {}, {action: action});
+
if (copy) {
entry.item = copy.barcode();
entry.item_id = copy.id();
if (patron) {
entry.patron_id = patron.id();
entry.user = patron.family_name();
- } else {
- entry.patron_id = (params as CheckoutParams).patron_id;
}
if (result.hold) {
<eg-copy-alert-manager #copyAlertManager></eg-copy-alert-manager>
-<!-- Worklog string identifiers should match #worlog_{{action}} -->
-<eg-string #worklog_checkout i18n-text text="Check Out"></eg-string>
-<eg-string #worklog_checkin i18n-text text="Check In"></eg-string>
-<eg-string #worklog_noncat_checkout i18n-text text="Noncataloged Checkout"></eg-string>
-<eg-string #worklog_renew i18n-text text="Renewal"></eg-string>
-<eg-string #worklog_requested_hold i18n-text text="Requested Hold"></eg-string>
-<eg-string #worklog_edited_patron i18n-text text="Edited Patron"></eg-string>
-<eg-string #worklog_registered_patron i18n-text text="Registered Patron"></eg-string>
-<eg-string #worklog_paid_bill i18n-text text="Paid Bill"></eg-string>
-
import {CopyInTransitDialogComponent} from './in-transit-dialog.component';
import {CopyAlertManagerDialogComponent
} from '@eg/staff/share/holdings/copy-alert-manager.component';
-import {WorkLogService, WorkLogEntry} from './work-log.service';
/* Container component for sub-components used by circulation actions.
*
@ViewChild('holdShelfStr') holdShelfStr: StringComponent;
@ViewChild('catalogingStr') catalogingStr: StringComponent;
- // Worklog string variable names have to match "worklog_{{action}}"
- @ViewChild('worklog_checkout') worklog_checkout: StringComponent;
- @ViewChild('worklog_checkin') worklog_checkin: StringComponent;
- @ViewChild('worklog_noncat_checkout') worklog_noncat_checkout: StringComponent;
- @ViewChild('worklog_renew') worklog_renew: StringComponent;
- @ViewChild('worklog_requested_hold') worklog_requested_hold: StringComponent;
- @ViewChild('worklog_edited_patron') worklog_edited_patron: StringComponent;
- @ViewChild('worklog_registered_patron') worklog_registered_patron: StringComponent;
- @ViewChild('worklog_paid_bill') worklog_paid_bill: StringComponent;
-
- constructor(
- private worklog: WorkLogService,
- private circ: CircService) {
+ constructor(private circ: CircService) {
this.circ.components = this;
- this.worklog.components = this;
}
}
+++ /dev/null
-import {Injectable} from '@angular/core';
-import {Observable, empty, from} from 'rxjs';
-import {map, concatMap, mergeMap} 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';
-import {EventService, EgEvent} from '@eg/core/event.service';
-import {AuthService} from '@eg/core/auth.service';
-import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
-import {AudioService} from '@eg/share/util/audio.service';
-import {CircEventsComponent} from './events-dialog.component';
-import {CircComponentsComponent} from './components.component';
-import {StringService} from '@eg/share/string/string.service';
-import {ServerStoreService} from '@eg/core/server-store.service';
-import {StoreService} from '@eg/core/store.service';
-import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
-
-export interface WorkLogEntry {
- when?: Date;
- msg?: string;
- action?: string;
- actor?: string // staff username
- item?: string; // barcode
- item_id?: number;
- user?: string; // patron family name
- patron_id?: number;
- hold_id?: number;
- amount?: number; // paid amount
-}
-
-
-@Injectable()
-export class WorkLogService {
-
- maxEntries: number = null;
- maxPatrons: number = null;
- components: CircComponentsComponent;
-
- constructor(
- private store: StoreService,
- private serverStore: ServerStoreService,
- private auth: AuthService
- ) {}
-
- loadSettings(): Promise<any> {
- return this.serverStore.getItemBatch([
- 'ui.admin.work_log.max_entries',
- 'ui.admin.patron_log.max_entries'
- ]).then(sets => {
- this.maxEntries = sets['ui.admin.work_log.max_entries'] || 20;
- this.maxPatrons = sets['ui.admin.patron_log.max_entries'] || 10;
- });
- }
-
- record(entry: WorkLogEntry) {
-
- if (this.maxEntries === null) {
- throw new Error('WorkLogService.loadSettings() required');
- return;
- }
-
- entry.when = new Date();
- entry.actor = this.auth.user().usrname();
- entry.msg = this.components[`worklog_${entry.action}`].text;
-
- const workLog = this.store.getLocalItem('eg.work_log') || [];
- let patronLog = this.store.getLocalItem('eg.patron_log') || [];
-
- workLog.push(entry);
- if (workLog.lenth > this.maxEntries) {
- workLog.shift();
- }
-
- console.log('HERE', workLog);
-
- this.store.setLocalItem('eg.work_log', workLog);
-
- if (entry.patron_id) {
- // Remove existing entries that match this patron
- patronLog = patronLog.filter(e => e.patron_id !== entry.patron_id);
-
- patronLog.push(entry);
- if (patronLog.length > this.maxPatrons) {
- patronLog.shift();
- }
-
- this.store.setLocalItem('eg.patron_log', patronLog);
- }
- }
-}
-
-
--- /dev/null
+
+<!-- Worklog string identifiers should match #worlog_{{action}} -->
+<eg-string #worklog_checkout i18n-text text="Check Out"></eg-string>
+<eg-string #worklog_checkin i18n-text text="Check In"></eg-string>
+<eg-string #worklog_noncat_checkout i18n-text text="Noncataloged Checkout"></eg-string>
+<eg-string #worklog_renew i18n-text text="Renewal"></eg-string>
+<eg-string #worklog_requested_hold i18n-text text="Requested Hold"></eg-string>
+<eg-string #worklog_edited_patron i18n-text text="Edited Patron"></eg-string>
+<eg-string #worklog_registered_patron i18n-text text="Registered Patron"></eg-string>
+<eg-string #worklog_paid_bill i18n-text text="Paid Bill"></eg-string>
+
--- /dev/null
+import {Component, OnInit, Output, Input, ViewChild, EventEmitter} from '@angular/core';
+import {StringComponent} from '@eg/share/string/string.component';
+import {WorkLogService, WorkLogEntry} from './worklog.service';
+
+/** Component for housing strings related to the worklog service
+ *
+ * NOTE: once we have in-code i18n support, this and our module
+ * can go away, leaving only the service
+ */
+
+
+@Component({
+ templateUrl: 'strings.component.html',
+ selector: 'eg-worklog-strings-components'
+})
+export class WorkLogStringsComponent {
+
+ // Worklog string variable names have to match "worklog_{{action}}"
+ @ViewChild('worklog_checkout') worklog_checkout: StringComponent;
+ @ViewChild('worklog_checkin') worklog_checkin: StringComponent;
+ @ViewChild('worklog_noncat_checkout') worklog_noncat_checkout: StringComponent;
+ @ViewChild('worklog_renew') worklog_renew: StringComponent;
+ @ViewChild('worklog_requested_hold') worklog_requested_hold: StringComponent;
+ @ViewChild('worklog_edited_patron') worklog_edited_patron: StringComponent;
+ @ViewChild('worklog_registered_patron') worklog_registered_patron: StringComponent;
+ @ViewChild('worklog_paid_bill') worklog_paid_bill: StringComponent;
+
+ constructor(private worklog: WorkLogService) {
+ this.worklog.workLogStrings = this;
+ }
+}
+
--- /dev/null
+import {NgModule} from '@angular/core';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {WorkLogService} from './worklog.service';
+import {WorkLogStringsComponent} from './strings.component';
+
+@NgModule({
+ declarations: [
+ WorkLogStringsComponent
+ ],
+ imports: [
+ StaffCommonModule
+ ],
+ exports: [
+ WorkLogStringsComponent
+ ],
+ providers: [
+ WorkLogService
+ ]
+})
+
+export class WorkLogModule {}
--- /dev/null
+import {Injectable} from '@angular/core';
+import {AuthService} from '@eg/core/auth.service';
+import {StringService} from '@eg/share/string/string.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {StoreService} from '@eg/core/store.service';
+import {WorkLogStringsComponent} from './strings.component';
+
+export interface WorkLogEntry {
+ when?: Date;
+ msg?: string;
+ action?: string;
+ actor?: string // staff username
+ item?: string; // barcode
+ item_id?: number;
+ user?: string; // patron family name
+ patron_id?: number;
+ hold_id?: number;
+ amount?: number; // paid amount
+}
+
+
+@Injectable()
+export class WorkLogService {
+
+ maxEntries: number = null;
+ maxPatrons: number = null;
+ workLogStrings: WorkLogStringsComponent = null;
+
+ constructor(
+ private store: StoreService,
+ private serverStore: ServerStoreService,
+ private auth: AuthService
+ ) {}
+
+ loadSettings(): Promise<any> {
+ return this.serverStore.getItemBatch([
+ 'ui.admin.work_log.max_entries',
+ 'ui.admin.patron_log.max_entries'
+ ]).then(sets => {
+ this.maxEntries = sets['ui.admin.work_log.max_entries'] || 20;
+ this.maxPatrons = sets['ui.admin.patron_log.max_entries'] || 10;
+ });
+ }
+
+ record(entry: WorkLogEntry) {
+
+ if (this.maxEntries === null) {
+ throw new Error('WorkLogService.loadSettings() required');
+ return;
+ }
+
+ if (this.workLogStrings === null) {
+ throw new Error(
+ 'Add <eg-worklog-strings-components/> to your component for worklog support');
+ return;
+ }
+
+ entry.when = new Date();
+ entry.actor = this.auth.user().usrname();
+ entry.msg = this.workLogStrings[`worklog_${entry.action}`].text;
+
+ const workLog: WorkLogEntry[] =
+ this.store.getLocalItem('eg.work_log') || [];
+
+ let patronLog: WorkLogEntry[] =
+ this.store.getLocalItem('eg.patron_log') || [];
+
+ workLog.push(entry);
+ if (workLog.length > this.maxEntries) {
+ workLog.shift();
+ }
+
+ this.store.setLocalItem('eg.work_log', workLog);
+
+ if (entry.patron_id) {
+ // Remove existing entries that match this patron
+ patronLog = patronLog.filter(e => e.patron_id !== entry.patron_id);
+
+ patronLog.push(entry);
+ if (patronLog.length > this.maxPatrons) {
+ patronLog.shift();
+ }
+
+ this.store.setLocalItem('eg.patron_log', patronLog);
+ }
+ }
+}
+
+