LP#1207533: item-oriented Triggered Event Log user/miker/lp1207533-triggered-events-log
authorMike Rylander <mrylander@gmail.com>
Mon, 22 Mar 2021 20:00:55 +0000 (16:00 -0400)
committerMike Rylander <mrylander@gmail.com>
Wed, 24 Mar 2021 19:09:31 +0000 (15:09 -0400)
This commit adds an item-oriented version of the Triggered Event Log
interface.  The primary interface differences between the two are:
  * Instead of showing the item barcodes for events related to a
  patron, it shows the patron barcodes for events related to an item
  * It is labeled as "Item Specific" instead of "Patron Specific"

The main interface addition can be tested in the same way as the parent
commit.  To get to the new item TEL interface, use the Actions menu from
the patron Items Out interface, the Item Status interface, or the
Holdings list in the record cataloging interface.

Signed-off-by: Mike Rylander <mrylander@gmail.com>
18 files changed:
Open-ILS/examples/fm_IDL.xml
Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-grid.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-grid.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/item/event-log/routing.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/item/routing.module.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/routing.module.ts
Open-ILS/src/perlmods/lib/OpenILS/Application/Trigger/Event.pm
Open-ILS/src/sql/Pg/040.schema.asset.sql
Open-ILS/src/sql/Pg/400.schema.action_trigger.sql
Open-ILS/src/sql/Pg/950.data.seed-values.sql
Open-ILS/src/sql/Pg/upgrade/ZZZZ.schema.item_triggered_event_log.sql [new file with mode: 0644]
Open-ILS/web/js/ui/default/staff/cat/catalog/app.js
Open-ILS/web/js/ui/default/staff/cat/item/app.js
Open-ILS/web/js/ui/default/staff/circ/patron/items_out.js
docs/RELEASE_NOTES_NEXT/Circulation/PatronTriggeredEventsLog.adoc

index 6f23d5c..87d8dcb 100644 (file)
@@ -1457,6 +1457,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="Context User Path" name="context_usr_path" reporter:datatype="text"/>
                        <field reporter:label="Context Library Path" name="context_library_path" reporter:datatype="text"/>
                        <field reporter:label="Context Bib Path" name="context_bib_path" reporter:datatype="text"/>
+                       <field reporter:label="Context Item Path" name="context_item_path" reporter:datatype="text"/>
                </fields>
                <links>
                        <link field="owner" reltype="has_a" key="id" map="" class="aou"/>
@@ -1554,6 +1555,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="Context User" name="context_user" reporter:datatype="link"/>
                        <field reporter:label="Context Library" name="context_library" reporter:datatype="link"/>
                        <field reporter:label="Context Bib" name="context_bib" reporter:datatype="link"/>
+                       <field reporter:label="Context Item" name="context_item" reporter:datatype="link"/>
                </fields>
                <links>
                        <link field="event_def" reltype="has_a" key="id" map="" class="atevdef"/>
@@ -1563,6 +1565,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <link field="context_user" reltype="has_a" key="id" map="" class="au"/>
                        <link field="context_library" reltype="has_a" key="id" map="" class="aou"/>
                        <link field="context_bib" reltype="has_a" key="id" map="" class="bre"/>
+                       <link field="context_item" reltype="has_a" key="id" map="" class="acp"/>
                </links>
        </class>
 
@@ -1696,6 +1699,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
             atev.context_user,
                        atev.context_library,
             atev.context_bib,
+            atev.context_item,
             rssr.title,
             rssr.author
                FROM action_trigger.event atev
@@ -1736,6 +1740,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <field reporter:label="Context User" name="context_user" reporter:datatype="link" />
                        <field reporter:label="Context Library" name="context_library" reporter:datatype="org_unit" />
                        <field reporter:label="Context Bib" name="context_bib" reporter:datatype="link" />
+                       <field reporter:label="Context Item" name="context_item" reporter:datatype="link" />
                        <field reporter:label="Title" name="title" reporter:datatype="text" />
                        <field reporter:label="Author" name="author" reporter:datatype="text" />
                </fields>
@@ -1749,6 +1754,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
                        <link field="context_user" reltype="has_a" key="id" map="" class="au" />
                        <link field="context_library" reltype="has_a" key="id" map="" class="aou" />
                        <link field="context_bib" reltype="has_a" key="id" map="" class="bre" />
+                       <link field="context_item" reltype="has_a" key="id" map="" class="acp" />
                </links>
                <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
                        <actions>
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-grid.component.html b/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-grid.component.html
new file mode 100644 (file)
index 0000000..63bd330
--- /dev/null
@@ -0,0 +1,15 @@
+<eg-grid #grid [dataSource]="gridSource" idlClass="atoul"
+  [sortable]="true"
+  [filterable]="true"
+  showFields="perm_lib,state,name,reactor,run_time,target_circ.usr.card.barcode,target_hold.usr.card.barcode,title,author"
+  ignoreFields="target_circ,target_hold"
+  persistKey="event_grid">
+  <eg-grid-toolbar-action label="Reset selected events" i18n-label
+    (onClick)="act_on_events('reset',$event)" [disableOnRows]="noRowSelected">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action label="Cancel selected events" i18n-label
+    (onClick)="act_on_events('cancel',$event)" [disableOnRows]="noRowSelected">
+  </eg-grid-toolbar-action>
+  <eg-grid-column *ngIf="event_type=='circ'" [sortable]="false" [filterable]="false" path="target_circ.usr.card.barcode"></eg-grid-column>
+  <eg-grid-column *ngIf="event_type=='hold'" [sortable]="false" [filterable]="false" path="target_hold.usr.card.barcode"></eg-grid-column>
+</eg-grid>
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-grid.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-grid.component.ts
new file mode 100644 (file)
index 0000000..6e393eb
--- /dev/null
@@ -0,0 +1,131 @@
+import {Component, EventEmitter, Input, Output, OnChanges, OnInit, ViewChild} from '@angular/core';
+import {Router} from '@angular/router';
+import {Observable, from, of} from 'rxjs';
+import {map, tap, switchMap, mergeMap} from 'rxjs/operators';
+import {AuthService} from '@eg/core/auth.service';
+import {FmRecordEditorComponent} from '@eg/share/fm-editor/fm-editor.component';
+import {FormatService} from '@eg/core/format.service';
+import {GridComponent} from '@eg/share/grid/grid.component';
+import {GridDataSource} from '@eg/share/grid/grid';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {EventService} from '@eg/core/event.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {Pager} from '@eg/share/util/pager';
+import {ToastService} from '@eg/share/toast/toast.service';
+import {NetService} from '@eg/core/net.service';
+import {OrgService} from '@eg/core/org.service';
+import {BibRecordService, BibRecordSummary} from '@eg/share/catalog/bib-record.service';
+
+// A filterable grid of A/T events for circ or ahr hook core types
+
+@Component({
+    selector: 'eg-item-event-grid',
+    templateUrl: './event-grid.component.html'
+})
+
+export class ItemEventGridComponent implements OnChanges, OnInit {
+
+    @Input() item: number;
+    @Input() event_type: string;
+
+    gridSource: GridDataSource;
+    numRowsSelected: number;
+
+    act_on_events: (action: string, rows: IdlObject[]) => void;
+    noRowSelected: (rows: IdlObject[]) => boolean;
+
+    @ViewChild('grid', { static: true }) grid: GridComponent;
+
+    constructor(
+        private idl: IdlService,
+        private auth: AuthService,
+        private bib: BibRecordService,
+        private format: FormatService,
+        private pcrud: PcrudService,
+        private router: Router,
+        private toast: ToastService,
+        private net: NetService,
+        private evt: EventService,
+        private org: OrgService
+    ) {
+
+    }
+
+    ngOnInit() {
+        this.gridSource = new GridDataSource();
+
+        this.gridSource.getRows = (pager: Pager, sort: any[]): Observable<IdlObject> => {
+        // TODO: why is this getting called twice on page load?
+
+            const orderBy: any = {atoul: 'id'};
+            if (sort.length) {
+                orderBy.atoul = sort[0].name + ' ' + sort[0].dir;
+            }
+
+            // base query to grab everything
+            const base: Object = {};
+            base[this.idl.classes['atoul'].pkey] = {'!=' : null};
+            base['context_item'] = (this.item ? this.item : {'>' : 0})
+
+            // circs or holds?
+            if (this.event_type == 'circ') {
+                base['target_circ'] = { '>' : 0 }
+            } else {
+                base['target_hold'] = { '>' : 0 }
+            }
+
+            const query: any = new Array();
+            query.push(base);
+
+            // and add any filters
+            Object.keys(this.gridSource.filters).forEach(key => {
+                Object.keys(this.gridSource.filters[key]).forEach(key2 => {
+                    query.push(this.gridSource.filters[key][key2]);
+                });
+            });
+
+            return this.pcrud.search('atoul',
+                query, {
+                flesh: 3,
+                flesh_fields: {
+                    atoul: ['target_circ', 'target_hold'],
+                    circ: ['target_copy','usr'],
+                    ahr: ['current_copy','usr'],
+                    au: ['card']
+                },
+                offset: pager.offset,
+                limit: pager.limit,
+                order_by: orderBy
+            });
+        };
+
+        this.act_on_events = (action: string, rows: IdlObject[]) => {
+            this.net.request(
+                'open-ils.actor',
+                'open-ils.actor.user.event.' + action + '.batch',
+                this.auth.token(), rows.map( event => event.id() )
+            ).subscribe(
+                (res) => {
+                    if (this.evt.parse(res)) {
+                        console.error('parsed error response',res);
+                    } else {
+                        console.log('success',res);
+                    }
+                },
+                (err) => {
+                    console.error('error',err);
+                },
+                () => {
+                    console.log('finis');
+                    this.grid.reload();
+                }
+            );
+        }
+
+        this.noRowSelected = (rows: IdlObject[]) => (rows.length == 0);
+    }
+
+    ngOnChanges() { this.reloadGrid(); }
+
+    reloadGrid() { this.grid.reload(); }
+}
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.component.html b/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.component.html
new file mode 100644 (file)
index 0000000..8b3c707
--- /dev/null
@@ -0,0 +1,20 @@
+
+<eg-staff-banner bannerText="Triggered Event Log (Item Specific)" i18n-bannerText>
+</eg-staff-banner>
+
+<ul ngbNav #nav="ngbNav" class="nav-tabs">
+  <li [ngbNavItem]="1">
+    <a ngbNavLink i18n>Circulations</a>
+    <ng-template ngbNavContent>
+      <eg-item-event-grid #itemEventGrid [item]="itemId" event_type="circ"></eg-item-event-grid>
+    </ng-template>
+  </li>
+  <li [ngbNavItem]="2">
+    <a ngbNavLink i18n>Holds</a>
+    <ng-template ngbNavContent>
+      <eg-item-event-grid #itemEventGrid [item]="itemId" event_type="hold"></eg-item-event-grid>
+    </ng-template>
+  </li>
+</ul>
+
+<div [ngbNavOutlet]="nav" class="mt-2"></div>
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.component.ts
new file mode 100644 (file)
index 0000000..2916ba8
--- /dev/null
@@ -0,0 +1,29 @@
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {ActivatedRoute} from '@angular/router';
+import {NetService} from '@eg/core/net.service';
+import {AuthService} from '@eg/core/auth.service';
+import {ItemEventGridComponent} from './event-grid.component';
+
+@Component({
+  templateUrl: 'event-log.component.html'
+})
+
+export class ItemEventLogComponent implements OnInit {
+    itemId: number;
+
+    @ViewChild('itemEventGrid', { static: true }) itemEventGrid: ItemEventGridComponent;
+
+    constructor(
+        private route: ActivatedRoute,
+        private net: NetService,
+        private auth: AuthService
+    ) {}
+
+    ngOnInit() {
+        // Note: if this is not supplied, the grid will show recent events
+        // across all items, which may be a neat feature...
+        this.itemId = +this.route.snapshot.paramMap.get('item');
+    }
+}
+
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/event-log.module.ts
new file mode 100644 (file)
index 0000000..67e1d59
--- /dev/null
@@ -0,0 +1,19 @@
+import {NgModule} from '@angular/core';
+import {StaffCommonModule} from '@eg/staff/common.module';
+import {ItemEventLogRoutingModule} from './routing.module';
+import {ItemEventGridComponent} from './event-grid.component';
+import {ItemEventLogComponent} from './event-log.component';
+
+@NgModule({
+  declarations: [
+    ItemEventGridComponent,
+    ItemEventLogComponent
+  ],
+  imports: [
+    StaffCommonModule,
+    ItemEventLogRoutingModule,
+  ],
+})
+
+export class ItemEventLogModule {}
+
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/item/event-log/routing.module.ts
new file mode 100644 (file)
index 0000000..fdb2ccf
--- /dev/null
@@ -0,0 +1,19 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+import {ItemEventLogComponent} from './event-log.component';
+
+const routes: Routes = [
+  { path: '',
+    component: ItemEventLogComponent
+  },
+  { path: ':item',
+    component: ItemEventLogComponent
+  },
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class ItemEventLogRoutingModule {}
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/item/routing.module.ts b/Open-ILS/src/eg2/src/app/staff/circ/item/routing.module.ts
new file mode 100644 (file)
index 0000000..e8045a9
--- /dev/null
@@ -0,0 +1,16 @@
+import {NgModule} from '@angular/core';
+import {RouterModule, Routes} from '@angular/router';
+
+const routes: Routes = [
+  { path: 'event-log',
+    loadChildren: () =>
+      import('./event-log/event-log.module').then(m => m.ItemEventLogModule)
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+
+export class CircItemRoutingModule {}
index fae4330..49cd79a 100644 (file)
@@ -5,6 +5,10 @@ const routes: Routes = [
   { path: 'patron',
     loadChildren: () =>
       import('./patron/routing.module').then(m => m.CircPatronRoutingModule)
+  },
+  { path: 'item',
+    loadChildren: () =>
+      import('./item/routing.module').then(m => m.CircItemRoutingModule)
   }
 ];
 
index 9a1693c..347164b 100644 (file)
@@ -524,38 +524,47 @@ sub build_environment {
         if ($self->event->event_def->context_usr_path) {
             my @usr_path = split(/\./, $self->event->event_def->context_usr_path);
             $self->_object_by_path( $self->target, undef, [qw/context usr/], \@usr_path );
+        }
 
-            if ($self->event->event_def->context_bib_path) {
-                my @bib_path = split(/\./, $self->event->event_def->context_bib_path);
-                $self->_object_by_path( $self->target, undef, [qw/context bib/], \@bib_path );
-                if (ref $self->environment->{context}->{bib} eq 'ARRAY') {
-                    $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->[0];
-                }
-                if ($self->environment->{context}->{bib}->isa('Fieldmapper::biblio::record_entry')) {
-                    $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->id;
-                } elsif ($self->environment->{context}->{bib}->isa('Fieldmapper::reporter::hold_request_record')) {
-                    $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->bib_record;
-                }
+        if ($self->event->event_def->context_bib_path) {
+            my @bib_path = split(/\./, $self->event->event_def->context_bib_path);
+            $self->_object_by_path( $self->target, undef, [qw/context bib/], \@bib_path );
+            if (ref $self->environment->{context}->{bib} eq 'ARRAY') {
+                $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->[0];
             }
-
-            if ($self->event->event_def->context_library_path) {
-                my @library_path = split(/\./, $self->event->event_def->context_library_path);
-                $self->_object_by_path( $self->target, undef, [qw/context org/], \@library_path );
-            } else {
-                $self->_object_by_path( $self->event->event_def, undef, [qw/context org/], ['owner'] );
+            if ($self->environment->{context}->{bib}->isa('Fieldmapper::biblio::record_entry')) {
+                $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->id;
+            } elsif ($self->environment->{context}->{bib}->isa('Fieldmapper::reporter::hold_request_record')) {
+                $self->environment->{context}->{bib} = $self->environment->{context}->{bib}->bib_record;
             }
-            $self->update_state(
-                $self->event->state, {
-                    'context_user' => $self->environment->{context}->{usr}
-                        ? $self->environment->{context}->{usr}->id
-                        : undef,
-                    'context_library' => $self->environment->{context}->{org}
-                        ? $self->environment->{context}->{org}->id
-                        : undef,
-                    'context_bib' => $self->environment->{context}->{bib}
-                }
-            );
         }
+
+        if ($self->event->event_def->context_library_path) {
+            my @library_path = split(/\./, $self->event->event_def->context_library_path);
+            $self->_object_by_path( $self->target, undef, [qw/context org/], \@library_path );
+        } else {
+            $self->_object_by_path( $self->event->event_def, undef, [qw/context org/], ['owner'] );
+        }
+
+        if ($self->event->event_def->context_item_path) {
+            my @item_path = split(/\./, $self->event->event_def->context_item_path);
+            $self->_object_by_path( $self->target, undef, [qw/context item/], \@item_path );
+        }
+
+        $self->update_state(
+            $self->event->state, {
+                'context_item' => $self->environment->{context}->{item}
+                    ? $self->environment->{context}->{item}->id
+                    : undef,
+                'context_user' => $self->environment->{context}->{usr}
+                    ? $self->environment->{context}->{usr}->id
+                    : undef,
+                'context_library' => $self->environment->{context}->{org}
+                    ? $self->environment->{context}->{org}->id
+                    : undef,
+                'context_bib' => $self->environment->{context}->{bib}
+            }
+        );
     
         $self->environment->{complete} = 1;
     } otherwise {
index 426b460..855c9a7 100644 (file)
@@ -950,9 +950,11 @@ DECLARE
     copy_id BIGINT;
 BEGIN
     EXECUTE 'SELECT ($1).' || quote_ident(TG_ARGV[0]) INTO copy_id USING NEW;
-    PERFORM * FROM asset.copy WHERE id = copy_id;
-    IF NOT FOUND THEN
-        RAISE EXCEPTION 'Key (%.%=%) does not exist in asset.copy', TG_TABLE_SCHEMA, TG_TABLE_NAME, copy_id;
+    IF copy_id IS NOT NULL THEN
+        PERFORM * FROM asset.copy WHERE id = copy_id;
+        IF NOT FOUND THEN
+            RAISE EXCEPTION 'Key (%.%=%) does not exist in asset.copy', TG_TABLE_SCHEMA, TG_TABLE_NAME, copy_id;
+        END IF;
     END IF;
     RETURN NULL;
 END;
index 80e4f8d..3a0816a 100644 (file)
@@ -200,6 +200,7 @@ CREATE TABLE action_trigger.event_definition (
     context_usr_path        TEXT, -- for optimizing action_trigger.event
     context_library_path    TEXT, -- '''
     context_bib_path        TEXT, -- '''
+    context_item_path       TEXT, -- '''
 
     message_template        TEXT,
     message_usr_path        TEXT,
@@ -282,7 +283,8 @@ CREATE TABLE action_trigger.event (
     async_output    BIGINT      REFERENCES action_trigger.event_output (id),
     context_user    INT         REFERENCES actor.usr (id),
     context_library INT         REFERENCES actor.org_unit (id),
-    context_bib     BIGINT      REFERENCES biblio.record_entry (id)
+    context_bib     BIGINT      REFERENCES biblio.record_entry (id),
+    context_item    BIGINT
 );
 CREATE INDEX atev_target_def_idx ON action_trigger.event (target,event_def);
 CREATE INDEX atev_def_state ON action_trigger.event (event_def,state);
@@ -291,6 +293,11 @@ CREATE INDEX atev_async_output ON action_trigger.event (async_output);
 CREATE INDEX atev_error_output ON action_trigger.event (error_output);
 CREATE INDEX atev_context_user ON action_trigger.event (context_user);
 CREATE INDEX atev_context_library ON action_trigger.event (context_library);
+CREATE INDEX atev_context_item ON action_trigger.event (context_item);
+
+CREATE TRIGGER action_trigger_event_context_item_fkey_trig
+  AFTER INSERT OR UPDATE ON action_trigger.event
+  FOR EACH ROW EXECUTE PROCEDURE evergreen.fake_fkey_tgr('context_item');
 
 CREATE TABLE action_trigger.event_params (
     id          BIGSERIAL   PRIMARY KEY,
index 32c5d8e..07533f1 100644 (file)
@@ -17349,7 +17349,8 @@ UPDATE
 SET
     context_usr_path = 'usr',
     context_library_path = 'circ_lib',
-    context_bib_path = 'target_copy.call_number.record'
+    context_bib_path = 'target_copy.call_number.record',
+    context_item_path = 'target_copy'
 WHERE
     hook IN (
         SELECT key FROM action_trigger.hook WHERE core_type = 'circ'
@@ -17361,7 +17362,8 @@ UPDATE
 SET
     context_usr_path = 'usr',
     context_library_path = 'pickup_lib',
-    context_bib_path = 'bib_rec'
+    context_bib_path = 'bib_rec',
+    context_item_path = 'current_copy'
 WHERE
     hook IN (
         SELECT key FROM action_trigger.hook WHERE core_type = 'ahr'
diff --git a/Open-ILS/src/sql/Pg/upgrade/ZZZZ.schema.item_triggered_event_log.sql b/Open-ILS/src/sql/Pg/upgrade/ZZZZ.schema.item_triggered_event_log.sql
new file mode 100644 (file)
index 0000000..4c721ca
--- /dev/null
@@ -0,0 +1,69 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('ZZZZ', :eg_version);
+
+DROP TRIGGER IF EXISTS action_trigger_event_context_item_trig ON action_trigger.event;
+
+-- Create a NULLABLE version of the fake-copy-fkey trigger function.
+CREATE OR REPLACE FUNCTION evergreen.fake_fkey_tgr () RETURNS TRIGGER AS $F$
+DECLARE
+    copy_id BIGINT;
+BEGIN
+    EXECUTE 'SELECT ($1).' || quote_ident(TG_ARGV[0]) INTO copy_id USING NEW;
+    IF copy_id IS NOT NULL THEN
+        PERFORM * FROM asset.copy WHERE id = copy_id;
+        IF NOT FOUND THEN
+            RAISE EXCEPTION 'Key (%.%=%) does not exist in asset.copy', TG_TABLE_SCHEMA, TG_TABLE_NAME, copy_id;
+        END IF;
+    END IF;
+    RETURN NULL;
+END;
+$F$ LANGUAGE PLPGSQL;
+
+
+--    context_item_path        TEXT, -- for optimizing action_trigger.event
+ALTER TABLE action_trigger.event_definition ADD COLUMN context_item_path TEXT;
+
+--    context_item     BIGINT      REFERENCES asset.copy (id)
+ALTER TABLE action_trigger.event ADD COLUMN context_item BIGINT;
+CREATE INDEX atev_context_item ON action_trigger.event (context_item);
+
+UPDATE
+    action_trigger.event_definition
+SET
+    context_item_path = 'target_copy'
+WHERE
+    hook IN (
+        SELECT key FROM action_trigger.hook WHERE core_type = 'circ'
+    )
+;
+
+UPDATE
+    action_trigger.event_definition
+SET
+    context_item_path = 'current_copy'
+WHERE
+    hook IN (
+        SELECT key FROM action_trigger.hook WHERE core_type = 'ahr'
+    )
+;
+
+-- Retroactively setting context_item on existing rows in action_trigger.event:
+-- This is not done by default because it'll likely take a long time depending on the Evergreen
+-- installation.  You may want to do this out-of-band with the upgrade if you want to do this at all.
+--
+-- \pset format unaligned
+-- \t
+-- \o update_action_trigger_events_for_circs.sql
+-- SELECT 'UPDATE action_trigger.event e SET context_item = c.target_copy FROM action.circulation cWHERE c.id = e.target AND e.id = ' || e.id || ' RETURNING ' || e.id || ';' FROM action_trigger.event e, action.circulation c WHERE e.target = c.id AND e.event_def IN (SELECT id FROM action_trigger.event_definition WHERE hook in (SELECT key FROM action_trigger.hook WHERE core_type = 'circ')) ORDER BY e.id DESC;
+-- \o
+-- \o update_action_trigger_events_for_holds.sql
+-- SELECT 'UPDATE action_trigger.event e SET context_item = h.current_copy FROM action.hold_request h WHERE h.id = e.target AND e.id = ' || e.id || ' RETURNING ' || e.id || ';' FROM action_trigger.event e, action.hold_request h WHERE e.target = h.id AND e.event_def IN (SELECT id FROM action_trigger.event_definition WHERE hook in (SELECT key FROM action_trigger.hook WHERE core_type = 'ahr')) ORDER BY e.id DESC;
+-- \o
+
+COMMIT;
+
+CREATE TRIGGER action_trigger_event_context_item_trig
+  AFTER INSERT OR UPDATE ON action_trigger.event
+  FOR EACH ROW EXECUTE PROCEDURE evergreen.fake_fkey_tgr('context_item');
+
index 5678a98..9c29f7d 100644 (file)
@@ -1658,8 +1658,7 @@ function($scope , $routeParams , $location , $window , $q , egCore , egHolds , e
         angular.forEach(
             gatherSelectedHoldingsIds(),
             function (cid) {
-                var url = egCore.env.basePath +
-                          'cat/item/' + cid + '/triggered_events';
+                var url = '/eg2/staff/circ/item/event-log/' + cid;
                 $timeout(function() { $window.open(url, '_blank') });
             }
         );
index dbf7176..f04ffa5 100644 (file)
@@ -161,7 +161,7 @@ function($scope , $q , $window , $location , $timeout , egCore , egNet , egGridD
     }
 
     $scope.show_triggered_events = function() {
-        $location.path('/cat/item/' + $scope.args.copyId + '/triggered_events');
+        window.open('/eg2/staff/circ/item/event-log/' + $scope.args.copyId, '_blank');
     }
 
     $scope.show_item_holds = function() {
@@ -469,7 +469,7 @@ function($scope , $q , $window , $location , $timeout , egCore , egNet , egGridD
     $scope.context.show_triggered_events = function() {
         var item = copyGrid.selectedItems()[0];
         if (item) 
-            $location.path('/cat/item/' + item.id + '/triggered_events');
+            window.open('/eg2/staff/circ/item/event-log/' + item.id, '_blank');
     }
 
     function gatherSelectedRecordIds () {
@@ -613,7 +613,7 @@ function($scope , $q , $window , $location , $timeout , egCore , egNet , egGridD
     $scope.selectedHoldingsItemStatusTgrEvt= function() {
         var item = copyGrid.selectedItems()[0];
         if (item)
-            $location.path('/cat/item/' + item.id + '/triggered_events');
+            window.open('/eg2/staff/circ/item/event-log/' + item.id, '_blank');
     }
 
     $scope.selectedHoldingsItemStatusHolds= function() {
@@ -1322,7 +1322,7 @@ console.debug($scope.copy_alert_count);
     }
 
     $scope.context.show_triggered_events = function() {
-        $location.path('/cat/item/' + copyId + '/triggered_events');
+        window.open('/eg2/staff/circ/item/event-log/' + copyId, '_blank');
     }
 
     loadCopy().then(loadTabData);
index 522a08d..8abea44 100644 (file)
@@ -463,11 +463,10 @@ function($scope , $q , $routeParams , $timeout , egCore , egUser , patronSvc ,
     $scope.show_triggered_events = function(items) {
         var focus = items.length == 1;
         angular.forEach(items, function(item) {
-            var url = egCore.env.basePath +
-                      '/cat/item/' +
-                      item.target_copy().id() +
-                      '/triggered_events';
+            var url = '/eg2/staff/circ/item/event-log/' +
+                      item.target_copy().id();
             $timeout(function() { var x = $window.open(url, '_blank'); if (focus) x.focus() });
+
         });
     }
 
index 7932fb7..23bc9c0 100644 (file)
@@ -1,5 +1,7 @@
-New Patron Triggered Events Log
+New Triggered Events Log
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-A reimplementation of the Patron Triggered Events Log interface along with
-supporting infrastructure for speedier results with large datasets.
+A reimplementation of both the Patron and Item Triggered Events Log
+interface along with supporting infrastructure for speedier results
+with large datasets.
+