LP1904036 work log continued
authorBill Erickson <berickxx@gmail.com>
Thu, 22 Apr 2021 16:00:45 +0000 (12:00 -0400)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:32 +0000 (20:13 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
20 files changed:
Open-ILS/src/eg2/src/app/staff/catalog/catalog.module.ts
Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.html
Open-ILS/src/eg2/src/app/staff/catalog/hold/hold.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/bills.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/edit-toolbar.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/edit.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/register.component.html
Open-ILS/src/eg2/src/app/staff/share/billing/billing.service.ts
Open-ILS/src/eg2/src/app/staff/share/circ/circ.module.ts
Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts
Open-ILS/src/eg2/src/app/staff/share/circ/components.component.html
Open-ILS/src/eg2/src/app/staff/share/circ/components.component.ts
Open-ILS/src/eg2/src/app/staff/share/circ/work-log.service.ts [deleted file]
Open-ILS/src/eg2/src/app/staff/share/worklog/strings.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/worklog/strings.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/worklog/worklog.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/worklog/worklog.service.ts [new file with mode: 0644]

index 58781da..77d19ca 100644 (file)
@@ -36,6 +36,7 @@ import {PreferencesComponent} from './prefs.component';
 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: [
@@ -76,7 +77,8 @@ import {BarcodesModule} from '@eg/staff/share/barcodes/barcodes.module';
     PatronModule,
     MarcEditModule,
     HttpClientModule
-    BarcodesModule
+    BarcodesModule,
+    WorkLogModule
   ],
   providers: [
     StaffCatalogService
index c04c970..b374fbf 100644 (file)
@@ -1,6 +1,7 @@
 
 <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
index ac616d7..3bd0fad 100644 (file)
@@ -22,6 +22,7 @@ import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
     } 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;
@@ -119,7 +120,8 @@ export class HoldComponent implements OnInit {
         private staffCat: StaffCatalogService,
         private holds: HoldsService,
         private patron: PatronService,
-        private perm: PermService
+        private perm: PermService,
+        private worklog: WorkLogService
     ) {
         this.holdContexts = [];
         this.smsCarriers = [];
@@ -151,7 +153,7 @@ export class HoldComponent implements OnInit {
                 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') {
@@ -638,6 +640,13 @@ export class HoldComponent implements OnInit {
                 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
index b9ff5db..d38210f 100644 (file)
@@ -27,6 +27,7 @@ import {AddBillingDialogComponent} from '@eg/staff/share/billing/billing-dialog.
 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',
@@ -84,6 +85,7 @@ export class BillsComponent implements OnInit, AfterViewInit {
         private circ: CircService,
         private billing: BillingService,
         private flatData: GridFlatDataService,
+        private worklog: WorkLogService,
         public patronService: PatronService,
         public context: PatronContextService
     ) {}
@@ -270,6 +272,12 @@ export class BillsComponent implements OnInit, AfterViewInit {
             );
         })
         .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);
         })
index e060344..ee90e0c 100644 (file)
@@ -91,7 +91,11 @@ export class CheckoutComponent implements OnInit, AfterViewInit {
     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) {
index fc1093b..f67751f 100644 (file)
@@ -6,7 +6,6 @@ import {IdlService, IdlObject} from '@eg/core/idl.service';
 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 {
@@ -49,8 +48,7 @@ export class EditToolbarComponent implements OnInit {
         private idl: IdlService,
         private net: NetService,
         private auth: AuthService,
-        private patronService: PatronService,
-        public context: PatronContextService
+        private patronService: PatronService
     ) {}
 
     ngOnInit() {
@@ -87,9 +85,9 @@ export class EditToolbarComponent implements OnInit {
         });
     }
 
-    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(
index 37c9bf3..3b1576d 100644 (file)
@@ -25,6 +25,7 @@ import {AlertDialogComponent} from '@eg/share/dialog/alert.component';
 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',
@@ -224,6 +225,7 @@ export class EditComponent implements OnInit, AfterViewInit {
         private broadcaster: BroadcastService,
         private patronService: PatronService,
         private printer: PrintService,
+        private worklog: WorkLogService,
         public context: PatronContextService
     ) {}
 
@@ -908,7 +910,7 @@ export class EditComponent implements OnInit, AfterViewInit {
             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':
@@ -1351,6 +1353,12 @@ export class EditComponent implements OnInit, AfterViewInit {
 
     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)});
index fb676fb..f2c2aa7 100644 (file)
@@ -50,6 +50,7 @@
 </ng-container>
 
 <eg-circ-components></eg-circ-components>
+<eg-worklog-strings-components></eg-worklog-strings-components>                
 
 <div class="row">
 
index 0417d3e..2d7d490 100644 (file)
@@ -32,6 +32,7 @@ import {HoldNotifyUpdateDialogComponent} from './hold-notify-update.component';
 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: [
@@ -67,7 +68,8 @@ import {BillingHistoryComponent} from './billing-history.component';
     BookingModule,
     PatronModule,
     PatronRoutingModule,
-    BarcodesModule
+    BarcodesModule,
+    WorkLogModule
   ],
   providers: [
     PatronResolver,
index f78b25a..57f3229 100644 (file)
@@ -1,6 +1,8 @@
 <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>
index 884eba2..0d65273 100644 (file)
@@ -101,7 +101,6 @@ export class BillingService {
                 return Promise.reject(evt);
             }
 
-            // TODO work log
             return response;
         });
     }
index 8a3602b..fef714a 100644 (file)
@@ -14,7 +14,7 @@ import {RouteDialogComponent} from './route-dialog.component';
 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: [
@@ -33,7 +33,8 @@ import {WorkLogService} from './work-log.service';
     imports: [
         StaffCommonModule,
         HoldingsModule,
-        BillingModule
+        BillingModule,
+        WorkLogModule
     ],
     exports: [
         CircGridComponent,
@@ -42,8 +43,7 @@ import {WorkLogService} from './work-log.service';
         CircComponentsComponent
     ],
     providers: [
-        CircService,
-        WorkLogService
+        CircService
     ]
 })
 
index 6dcbcef..3d4d453 100644 (file)
@@ -14,7 +14,7 @@ import {CircComponentsComponent} from './components.component';
 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;
@@ -130,6 +130,7 @@ export interface CheckoutParams {
     // internal tracking
     _override?: boolean;
     _renewal?: boolean;
+    _worklog?: WorkLogEntry;
 }
 
 export interface CircResultCommon {
@@ -175,6 +176,7 @@ export interface CheckinParams {
 
     // internal / local values that are moved from the API request.
     _override?: boolean;
+    _worklog?: WorkLogEntry;
 }
 
 export interface CheckinResult extends CircResultCommon {
@@ -429,7 +431,9 @@ export class CircService {
         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;
         });
     }
@@ -843,12 +847,14 @@ export class CircService {
     }
 
     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();
@@ -860,8 +866,6 @@ export class CircService {
         if (patron) {
             entry.patron_id = patron.id();
             entry.user = patron.family_name();
-        } else {
-            entry.patron_id = (params as CheckoutParams).patron_id;
         }
 
         if (result.hold) {
index 54bb765..a937b4b 100644 (file)
 
 <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>
-
index 5b9a51c..76dc5f5 100644 (file)
@@ -9,7 +9,6 @@ import {RouteDialogComponent} from './route-dialog.component';
 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.
  *
@@ -38,21 +37,8 @@ export class CircComponentsComponent {
     @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;
     }
 }
 
diff --git a/Open-ILS/src/eg2/src/app/staff/share/circ/work-log.service.ts b/Open-ILS/src/eg2/src/app/staff/share/circ/work-log.service.ts
deleted file mode 100644 (file)
index 724b0fb..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-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);
-        }
-    }
-}
-
-
diff --git a/Open-ILS/src/eg2/src/app/staff/share/worklog/strings.component.html b/Open-ILS/src/eg2/src/app/staff/share/worklog/strings.component.html
new file mode 100644 (file)
index 0000000..9a09918
--- /dev/null
@@ -0,0 +1,11 @@
+
+<!-- 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>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/worklog/strings.component.ts b/Open-ILS/src/eg2/src/app/staff/share/worklog/strings.component.ts
new file mode 100644 (file)
index 0000000..f824293
--- /dev/null
@@ -0,0 +1,32 @@
+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;
+    }
+}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/share/worklog/worklog.module.ts b/Open-ILS/src/eg2/src/app/staff/share/worklog/worklog.module.ts
new file mode 100644 (file)
index 0000000..c6c61df
--- /dev/null
@@ -0,0 +1,21 @@
+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 {}
diff --git a/Open-ILS/src/eg2/src/app/staff/share/worklog/worklog.service.ts b/Open-ILS/src/eg2/src/app/staff/share/worklog/worklog.service.ts
new file mode 100644 (file)
index 0000000..5d284d3
--- /dev/null
@@ -0,0 +1,89 @@
+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);
+        }
+    }
+}
+
+