LP1936233 Item status list view
authorBill Erickson <berickxx@gmail.com>
Thu, 22 Jul 2021 21:21:16 +0000 (17:21 -0400)
committerBill Erickson <berickxx@gmail.com>
Thu, 22 Jul 2021 21:21:16 +0000 (17:21 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/cat/item/status.component.html
Open-ILS/src/eg2/src/app/staff/cat/item/status.component.ts

index 0b184e5..df57ac8 100644 (file)
@@ -26,7 +26,7 @@
   text="There is no corresponding purchase order for this item.">
 </eg-string>
 
-<div class="row">
+<div class="row mb-3">
   <div class="col-lg-12 form-inline d-flex">
     <div class="input-group">
       <div class="input-group-prepend">
       helpText="Single barcode or list of barcodes separated with commas.">
     </eg-help-popover>
 
+    <span class="ml-2 mr-2 font-weight-bold" i18n>-- OR --</span>
+
     <eg-help-popover placement="top" i18n-helpText 
       helpText="File Format: One barcode per line. 
         All whitespace and commas will be removed before processing.">
     </eg-help-popover>
 
+    <input #fileSelector (change)="fileSelected($event)" 
+      id="upload-file" required class="ml-2" type="file"/>
+
     <div class="flex-1"></div>
 
     <button *ngIf="tab == 'list'" 
@@ -60,7 +65,6 @@
       List View
     </button>
 
-
     <!-- ACTIONS MENU -->
     <eg-grouped-menu i18n-label label="Actions" *ngIf="item && tab != 'list'">
 
 
       <!-- Show -->
       <eg-grouped-menu-entry i18n-label label="Show Record Holds"
-        i18n-group group="Show" [newTab]="true"
-        routerLink="/staff/catalog/record/{{item.call_number().record().id()}}/holds">
+        i18n-group group="Show" (entryClicked)="showRecordHolds([item])">
       </eg-grouped-menu-entry>
       <eg-grouped-menu-entry i18n-label label="Show In Catalog"
-        i18n-group group="Show" [newTab]="true"
-        routerLink="/staff/catalog/record/{{item.call_number().record().id()}}">
+        i18n-group group="Show" (entryClicked)="showInCatalog([item])">
       </eg-grouped-menu-entry>
       <eg-grouped-menu-entry i18n-label label="Show Originating Acquisition"
         i18n-group group="Show" (entryClicked)="showAcq([item])">
 
     </eg-grouped-menu>
 
-    <!-- grouped menu todo -->
-
   </div>
 </div>
 
 
 <eg-grid *ngIf="tab == 'list'" #grid [dataSource]="dataSource" idlClass="acp"
   (onRowActivate)="showDetails($event)" [cellTextGenerator]="cellTextGenerator"
-  [useLocalSort]="true" [sortable]="true" [showDeclaredFieldsOnly]="true">
-
+  [useLocalSort]="true" [sortable]="true" [showDeclaredFieldsOnly]="true"
+  [disablePaging]="true">
+
+  <eg-grid-toolbar-button i18n-label label="Clear List" (onClick)="clearList()">
+  </eg-grid-toolbar-button>
+
+  <!-- Un-grouped -->
+  <eg-grid-toolbar-action i18n-label label="Request Items"
+    (onClick)="requestItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label 
+    label="Link as Conjoined to Marked Bib Record"
+    (onClick)="openConjoinedDialog($event)">
+  </eg-grid-toolbar-action>
+
+  <eg-grid-toolbar-action i18n-label label="Update Inventory"
+    (onClick)="updateInventory($event)">
+  </eg-grid-toolbar-action>
+
+  <eg-grid-toolbar-action i18n-label label="Print Labels"
+    (onClick)="printLabels($event)">
+  </eg-grid-toolbar-action>
+
+  <!-- Add -->
+  <eg-grid-toolbar-action i18n-label label="Add Item To Bucket" 
+    i18n-group group="Add" (onClick)="addItemToBucket($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Add Record To Bucket" 
+    i18n-group group="Add" (onClick)="addRecordToBucket($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action
+    i18n-group group="Add" i18n-label label="Add Items"
+    (onClick)="addItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action
+    i18n-group group="Add" i18n-label label="Add Call Numbers and Items"
+    (onClick)="addVols($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action
+    i18n-group group="Add" i18n-label label="Add Item Alerts"
+    (onClick)="itemAlerts($event, 'create')">
+  </eg-grid-toolbar-action>
+
+  <!-- Edit -->
+  <eg-grid-toolbar-action i18n-label label="Delete Items"
+    i18n-group group="Edit" (onClick)="deleteItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Edit Call Numbers"
+    i18n-group group="Edit" (onClick)="editVols($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Edit Items"
+    i18n-group group="Edit" (onClick)="editItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Edit Call Numbers and Items"
+    i18n-group group="Edit" (onClick)="editVolsAndItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Replace Barcodes"
+    i18n-group group="Edit" (onClick)="replaceBarcodes($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action
+    i18n-group group="Edit" i18n-label label="Manage Item Alerts"
+    (onClick)="itemAlerts($event, 'manage')">
+  </eg-grid-toolbar-action>
+
+  <!-- Mark -->
+  <eg-grid-toolbar-action i18n-label label="Mark Item as Damaged"
+    i18n-group group="Mark" (onClick)="markDamaged($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Mark Item as Discard/Weed"
+    i18n-group group="Mark" (onClick)="discardWeed($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Mark Item as Missing"
+    i18n-group group="Mark" (onClick)="markMissing($event)">
+  </eg-grid-toolbar-action>
+
+  <!-- Show -->
+  <eg-grid-toolbar-action i18n-label label="Show Record Holds"
+    i18n-group group="Show" (onClick)="showRecordHolds($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Show In Catalog"
+    i18n-group group="Show" (onClick)="showInCatalog($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Show Originating Acquisition"
+    i18n-group group="Show" (onClick)="showAcq($event)">
+  </eg-grid-toolbar-action>
+
+  <!-- Booking -->
+  <eg-grid-toolbar-action i18n-label label="Make Items Bookable"
+    i18n-group group="Booking" (onClick)="makeItemsBookable($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Book Item Now"
+    i18n-group group="Booking" (onClick)="bookItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Manage Reservations"
+    i18n-group group="Booking" (onClick)="manageReservations($event)">
+  </eg-grid-toolbar-action>
+
+  <!-- Circulation -->
+  <eg-grid-toolbar-action i18n-label label="Check In Items"
+    i18n-group group="Circulation" (onClick)="checkinItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Renew Items"
+    i18n-group group="Circulation" (onClick)="renewItems($event)">
+  </eg-grid-toolbar-action>
+  <eg-grid-toolbar-action i18n-label label="Cancel Transit"
+    i18n-group group="Circulation" (onClick)="cancelTransits($event)">
+  </eg-grid-toolbar-action>
+
+  <!-- Transfer -->
+  <eg-grid-toolbar-action i18n-label 
+    label="Transfer Items to Previously Marked Library"
+    i18n-group group="Transfer" (onClick)="transferItemsToLib($event)">
+  </eg-grid-toolbar-action>
+
+  <eg-grid-toolbar-action i18n-label 
+    label="Transfer Items to Previously Marked Call Number"
+    i18n-group group="Transfer" (onClick)="transferItemsToCn($event)">
+  </eg-grid-toolbar-action>
+
+  <!-- columns -->
+
+  <eg-grid-column path="_index" [index]="true" [hidden]="true"></eg-grid-column>
   <eg-grid-column path="id" [hidden]="true"></eg-grid-column>
   <eg-grid-column path="alert_message"></eg-grid-column>
   <eg-grid-column path="barcode"></eg-grid-column>
index 7af2f61..3dd047a 100644 (file)
@@ -1,4 +1,5 @@
 import {Component, Input, OnInit, AfterViewInit, ViewChild} from '@angular/core';
+import {Location} from '@angular/common';
 import {Router, ActivatedRoute, ParamMap} from '@angular/router';
 import {from, of, empty} from 'rxjs';
 import {concatMap, tap} from 'rxjs/operators';
@@ -56,6 +57,9 @@ import {ItemStatusService} from './item.service';
 })
 
 export class ItemStatusComponent implements OnInit, AfterViewInit {
+    // Use for grid row indexes, since the grid may include multiple
+    // copies of a given item.
+    static rowIndex = 0;
 
     currentItemId: number;
     itemBarcode: string;
@@ -102,6 +106,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
     constructor(
         private router: Router,
         private route: ActivatedRoute,
+        private ngLocation: Location,
         private net: NetService,
         private idl: IdlService,
         private printer: PrintService,
@@ -149,29 +154,27 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
                         row.call_number().label() + ' ' +
                         row.call_number().suffix().label();
             }
-        }
-
-        this.worklog.loadSettings().then(_ => this.load(true));
-    }
-
-    load(first?: boolean) {
+        };
 
-        this.cat.fetchCcvms()
+        this.worklog.loadSettings()
+        .then(_ => this.cat.fetchCcvms())
         .then(_ => this.cat.fetchCmfs())
         .then(_ => {
+
             if (this.currentItemId) {
-                return this.getItemById(this.currentItemId);
+
+                return this.getItemById(this.currentItemId)
+                .then(item => this.item = item);
+
             } else if (this.preloadCopyIds) {
+
                 return from(this.preloadCopyIds).pipe(concatMap(id => {
                     return of(this.getItemById(id));
-                })).toPromise().then(_ => this.preloadCopyIds = null);
+                })).toPromise().then(__ => this.preloadCopyIds = null);
             }
         })
         .then(_ => {
 
-            // Avoid multiple subscriptions
-            if (!first) { return; }
-
             // Avoid watching for changes until after ngOnInit is complete
             // so we don't grab the same copy twice.
             this.route.paramMap.subscribe((params: ParamMap) => {
@@ -186,6 +189,29 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         });
     }
 
+    // Refresh items that were acted on by a grid action and splice them.
+    refreshSelectCopies(copies: IdlObject[]): Promise<any> {
+        return from(copies).pipe(concatMap(copy => {
+            const promise = this.getItemById(copy.id(), true)
+            .then(updatedCopy => {
+                if (this.item && updatedCopy.id() === this.item.id()) {
+                    this.item = updatedCopy;
+                }
+                this.itemService.scannedItems.forEach((item, idx) => {
+                    if (item.id() === updatedCopy.id()) {
+                        this.itemService.scannedItems.splice(idx, 1, updatedCopy);
+                    }
+                });
+            });
+
+            return from(promise);
+        })).toPromise().then(_ => {
+            if (this.grid) {
+                this.grid.reload();
+            }
+        });
+    }
+
     ngAfterViewInit() {
         this.selectInput();
     }
@@ -194,6 +220,30 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         this.router.navigate([`/staff/cat/item/${this.currentItemId}/${evt.nextId}`]);
     }
 
+    fileSelected($event) {
+        const file: File = $event.target.files[0];
+        const reader = new FileReader();
+
+        reader.onload = e => {
+            const list = e.target.result as string;
+            if (!list) { return; }
+
+            const barcodes = [];
+            list.split(/\r?\n/).forEach(line => {
+                line = line.replace(/[\s,]+/g, '');
+                if (line) {
+                    barcodes.push(line);
+                }
+            });
+
+            if (barcodes.length > 0) {
+                this.getItemsFromBarcodes(barcodes);
+            }
+        };
+
+        reader.readAsText(file);
+    }
+
     getItemFromBarcodeInput(): Promise<any> {
         this.currentItemId = null;
         this.item = null;
@@ -203,41 +253,51 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         // The barcode may be a comma-separated list of values.
         const barcodes = [];
         this.itemBarcode.split(/,/).forEach(bc => {
-            bc = bc.replace(/[\s,]+/g,'');
+            bc = bc.replace(/[\s,]+/g, '');
             if (bc) { barcodes.push(bc); }
         });
 
+        return this.getItemsFromBarcodes(barcodes);
+    }
+
+    getItemsFromBarcodes(barcodes: string[]): Promise<any> {
         let index = 0;
+
         return from(barcodes).pipe(concatMap(bc => {
-            return of(
-                this.getOneItemFromBarcode(bc)
-                .then(_ => {
-                    if (++index < barcodes.length) { return; }
-                    if (this.tab === 'list') { return; }
-
-                    // When entering multiple items via input or file
-                    // on a non-list page, show the detail view of the
-                    // last item loaded.
-                    if (this.itemService.scannedItems.length > 0) {
-                        const id = this.itemService.scannedItems[0].id();
-                        this.router.navigate([`/staff/cat/item/${id}/${this.tab}`]);
-                    }
-                })
-            );
+
+            const promise = this.getOneItemFromBarcode(bc)
+            .then(_ => {
+                if (++index < barcodes.length) { return; }
+                if (this.tab === 'list') { return; }
+
+                // When entering multiple items via input or file
+                // on a non-list page, show the detail view of the
+                // last item loaded.
+                if (this.itemService.scannedItems.length > 0) {
+                    this.item = this.itemService.scannedItems[0];
+                    const id = this.item.id();
+                    this.router.navigate([`/staff/cat/item/${id}/${this.tab}`]);
+                }
+            });
+
+            return from(promise);
+
         })).toPromise();
     }
 
     getOneItemFromBarcode(barcode: string): Promise<any> {
         return this.barcodeSelect.getBarcode('asset', barcode)
         .then(res => {
-            if (!res.id) {
+            if (!res) {
+                // Dialog was canceled, nothing to do
+            } else if (!res.id) {
                 this.noSuchItem = true;
             } else {
                 this.itemBarcode = null;
                 if (this.tab === 'list') {
                     this.selectInput();
-                    return this.getItemById(res.id);
                 }
+                return this.getItemById(res.id);
             }
         });
     }
@@ -250,9 +310,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         });
     }
 
-    getItemById(id: number): Promise<any> {
-
-        // TODO fetch open circ and store it on the copy
+    getItemById(id: number, fetchOnly?: boolean): Promise<any> {
 
         const flesh = {
             flesh : 4,
@@ -275,19 +333,21 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
 
         return this.pcrud.retrieve('acp', id, flesh)
         .toPromise().then(item => {
-            this.item = item;
-            this.mungeIsbns();
+            this.mungeIsbns(item);
             this.selectInput();
+            item._index = ItemStatusComponent.rowIndex++;
 
-            if (this.tab === 'list') {
-                this.itemService.scannedItems.unshift(item);
-            } else {
-                // Only add the copy to the scanned items list on a non-list
-                // page when the item does not already exist in the list.
-                const existing = this.itemService.scannedItems
-                    .filter(c => c.id() === item.id())[0];
-                if (!existing) {
+            if (!fetchOnly) {
+                if (this.tab === 'list') {
                     this.itemService.scannedItems.unshift(item);
+                } else {
+                    // Only add the copy to the scanned items list on a non-list
+                    // page when the item does not already exist in the list.
+                    const existing = this.itemService.scannedItems
+                        .filter(c => c.id() === item.id())[0];
+                    if (!existing) {
+                        this.itemService.scannedItems.unshift(item);
+                    }
                 }
             }
 
@@ -295,6 +355,8 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
                 if (this.grid) {
                     this.grid.reload();
                 }
+
+                return item;
             });
         });
     }
@@ -310,8 +372,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
     }
 
     // A bit of cleanup to make the ISBN's look friendlier
-    mungeIsbns() {
-        const item = this.item;
+    mungeIsbns(item: IdlObject) {
         const isbn = item.call_number().record().simple_record().isbn();
         if (isbn) {
             const matches = isbn.match(/"(.*?)"/g);
@@ -364,8 +425,18 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
 
     requestItems(copies: IdlObject[]) {
         if (copies.length === 0) { return; }
+
         const params = {target: copies.map(c => c.id()), holdFor: 'staff'};
-        this.router.navigate(['/staff/catalog/hold/C'], {queryParams: params});
+
+        const url = this.ngLocation.prepareExternalUrl(
+            this.router.serializeUrl(
+                this.router.createUrlTree(
+                    ['/staff/catalog/hold/C'], {queryParams: params}
+                )
+            )
+        );
+
+        window.open(url);
     }
 
     openConjoinedDialog(copies: IdlObject[]) {
@@ -397,7 +468,10 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         }
 
         this.deleteHolding.callNums = Object.values(callNumHash);
-        this.deleteHolding.open({size: 'sm'}).subscribe(modified => this.load());
+        this.deleteHolding.open({size: 'sm'})
+        .subscribe(modified => {
+            if (modified) { this.refreshSelectCopies(copies); }
+        });
     }
 
     checkinItems(copies: IdlObject[]) {
@@ -419,7 +493,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
             },
             () => {
                 dialog.close();
-                this.load();
+                this.refreshSelectCopies(copies);
             }
         );
     }
@@ -443,7 +517,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
             },
             () => {
                 dialog.close();
-                this.load();
+                this.refreshSelectCopies(copies);
             }
         );
     }
@@ -453,6 +527,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
 
         // Copies in transit are not always accompanied by their transit.
         const transitIds = [];
+        let modified = false;
         from(copies).pipe(concatMap(c => {
             return from(
                 this.circ.findCopyTransitById(c.id())
@@ -472,7 +547,15 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
                 return empty();
             }
 
-        })).subscribe();
+        })).subscribe(
+            changes => {
+                if (changes) { modified = true; }
+            },
+            null,
+            () => {
+                if (modified) { this.refreshSelectCopies(copies); }
+            }
+        );
     }
 
     updateInventory(copies: IdlObject[]) {
@@ -482,7 +565,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
             'open-ils.circ',
             'open-ils.circ.circulation.update_latest_inventory',
             this.auth.token(), {copy_list: copies.map(c => c.id())}
-        ).toPromise().then(_ => this.load());
+        ).toPromise().then(_ => this.refreshSelectCopies(copies));
     }
 
     printLabels(copies: IdlObject[]) {
@@ -524,7 +607,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
             return this.markDamagedDialog.open({size: 'lg'})
             .pipe(tap(ok => { if (ok) { modified = true; } }));
 
-        })).toPromise().then(_ => this.load());
+        })).toPromise().then(_ => this.refreshSelectCopies(copies));
     }
 
 
@@ -539,7 +622,9 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
             copyId => modified = true,
             null,
             () => {
-                if (modified) { this.load(); }
+                if (modified) {
+                    this.refreshSelectCopies(copies);
+                }
             }
         );
     }
@@ -556,7 +641,9 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
             copyId => modified = true,
             null,
             () => {
-                if (modified) { this.load(); }
+                if (modified) {
+                    this.refreshSelectCopies(copies);
+                }
             }
         );
     }
@@ -586,6 +673,25 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         this.holdings.spawnAddHoldingsUi(null, callNumIds, null, copyIds);
     }
 
+    showInCatalog(copies: IdlObject[]) {
+        if (copies.length === 0) { return; }
+
+        const recId = copies[0].call_number().record().id();
+        const url = this.ngLocation.prepareExternalUrl(
+            `/staff/catalog/record/${recId}`);
+
+        window.open(url);
+    }
+
+    showRecordHolds(copies: IdlObject[]) {
+        if (copies.length === 0) { return; }
+
+        const recId = copies[0].call_number().record().id();
+        const url = this.ngLocation.prepareExternalUrl(
+            `/staff/catalog/record/${recId}/holds`);
+
+        window.open(url);
+    }
 
     // Only the first item is used as the basis for new
     // call numbers.
@@ -605,7 +711,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         this.copyAlertsDialog.open({size: 'lg'}).subscribe(
             modified => {
                 if (modified) {
-                    this.load();
+                    this.refreshSelectCopies(copies);
                 }
             }
         );
@@ -617,7 +723,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         this.replaceBarcode.open({}).subscribe(
             modified => {
                 if (modified) {
-                    this.load();
+                    this.refreshSelectCopies(copies);
                 }
             }
         );
@@ -638,7 +744,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
 
         copies = this.idl.clone(copies); // avoid tweaking active data
         this.transferItems.autoTransferItems(copies, recId, orgId)
-        .then(success => success ? this.load() : null);
+        .then(success => success ?  this.refreshSelectCopies(copies) : null);
     }
 
 
@@ -653,7 +759,7 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         }
 
         this.transferItems.transferItems(copies.map(c => c.id()), cnId)
-        .then(success => success ? this.load() : null);
+        .then(success => success ?  this.refreshSelectCopies(copies) : null);
     }
 
     showDetails(copy?: IdlObject) {
@@ -662,23 +768,39 @@ export class ItemStatusComponent implements OnInit, AfterViewInit {
         if (copy) {
             // Row doubleclick
             copyId = copy.id();
-        } else if (this.grid) {
+
+        } else if (this.itemService.scannedItems.length > 0) {
             // Row select + clicking the Show Details button
-            copyId = this.grid.context.rowSelector.selected()[0];
-        }
 
-        // No item selected.  Use the first one in the list if we have one.
-        if (!copyId && this.itemService.scannedItems.length > 0) {
-            copyId = this.itemService.scannedItems[0].id();
+            // Grid row selector does not maintain order, so use the first
+            // grid row that is selected.
+            this.itemService.scannedItems.some(item => {
+                if (this.grid.context.rowIsSelected(item)) {
+                    copyId = item.id();
+                    return true;
+                }
+            });
+
+            // No rows selected, use the first copy row.
+            if (!copyId) {
+                copyId = this.itemService.scannedItems[0].id();
+            }
         }
 
-        this.router.navigate([`/staff/cat/item/${copyId}/summary`]);
+        if (copyId) {
+            this.router.navigate([`/staff/cat/item/${copyId}/summary`]);
+        }
     }
 
     showList() {
         this.currentItemId = null;
         this.router.navigate(['/staff/cat/item/list']);
     }
+
+    clearList() {
+        this.itemService.scannedItems = [];
+        this.grid.reload();
+    }
 }