</div>
<input type="text" class="form-control" id="item-barcode-input"
(keydown)="noSuchItem=false; true;"
- (keyup.enter)="getItemByBarcode()" [(ngModel)]="itemBarcode"
+ (keyup.enter)="getItemFromBarcodeInput()" [(ngModel)]="itemBarcode"
aria-describedby="barcode-label"/>
</div>
<button class="btn btn-outline-dark mr-1"
- (click)="getItemByBarcode()" i18n>Submit</button>
+ (click)="getItemFromBarcodeInput()" i18n>Submit</button>
<eg-help-popover placement="top" i18n-helpText
helpText="Single barcode or list of barcodes separated with commas.">
</eg-help-popover>
+ <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>
+
<div class="flex-1"></div>
<button *ngIf="tab == 'list'"
{{r.call_number().suffix().label()}}
</ng-template>
-<eg-grid *ngIf="tab == 'list'" #grid [dataSource]="dataSource"
+<eg-grid *ngIf="tab == 'list'" #grid [dataSource]="dataSource" idlClass="acp"
+ (onRowActivate)="showDetails($event)" [cellTextGenerator]="cellTextGenerator"
[useLocalSort]="true" [sortable]="true" [showDeclaredFieldsOnly]="true">
- <eg-grid-column i18n-label label="Item ID" path="id" [index]="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>
- <eg-grid-column i18n-label label="Alert Message" path="alert_message">
+ <eg-grid-column i18n-label label="Location" path="location.name">
</eg-grid-column>
- <eg-grid-column i18n-label label="Barcode" path="barcode">
+ <eg-grid-column i18n-label label="Item Status" path="status.name">
</eg-grid-column>
- <eg-grid-column i18n-label label="Location" path="location.name">
- </eg-grid-column>
<eg-grid-column i18n-label label="Due Date" path="_circ.due_date"
timezoneContextOrg="_circ.circ_lib" dateOnlyIntervalField="_circ.duration"
<eg-grid-column i18n-label label="Title"
path="call_number.record.simple_record.title"></eg-grid-column>
- <eg-grid-column i18n-label label="Author"
- path="call_number.record.simple_record.author"></eg-grid-column>
-
<eg-grid-column path="call_number.*" [hidden]="true"></eg-grid-column>
+ <eg-grid-column path="call_number.record.*" [hidden]="true"></eg-grid-column>
<eg-grid-column path="call_number.record.simple_record.*" [hidden]="true">
</eg-grid-column>
export class ItemStatusComponent implements OnInit, AfterViewInit {
- itemId: number;
+ currentItemId: number;
itemBarcode: string;
noSuchItem = false;
item: IdlObject;
tab: string;
preloadCopyIds: number[];
+ cellTextGenerator: GridCellTextGenerator;
dataSource: GridDataSource = new GridDataSource();
@ViewChild('grid') private grid: GridComponent;
ngOnInit() {
- this.itemId = +this.route.snapshot.paramMap.get('id');
+ this.currentItemId = +this.route.snapshot.paramMap.get('id');
this.tab = this.route.snapshot.paramMap.get('tab');
const copyIdList = this.route.snapshot.paramMap.get('copyIdList');
- if (copyIdList) {
+
+ if (copyIdList && !this.itemService.preloadItemsLoaded) {
+ this.itemService.preloadItemsLoaded = true;
this.preloadCopyIds = copyIdList.split(',').map(id => Number(id));
}
if (!this.tab) {
- if (this.itemId) {
+ if (this.currentItemId) {
this.tab = 'summary';
} else {
this.tab = 'list';
}
}
- this.worklog.loadSettings().then(_ => this.load(true));
-
this.dataSource.getRows = (pager: Pager, sort: any[]) => {
return from(this.itemService.scannedItems);
};
+
+ this.cellTextGenerator = {
+ call_number_label: row => {
+ return row.call_number().prefix().label() + ' ' +
+ row.call_number().label() + ' ' +
+ row.call_number().suffix().label();
+ }
+ }
+
+ this.worklog.loadSettings().then(_ => this.load(true));
}
load(first?: boolean) {
this.cat.fetchCcvms()
.then(_ => this.cat.fetchCmfs())
.then(_ => {
- if (this.itemId) {
- return this.getItemById(this.itemId);
+ if (this.currentItemId) {
+ return this.getItemById(this.currentItemId);
} else if (this.preloadCopyIds) {
return from(this.preloadCopyIds).pipe(concatMap(id => {
return of(this.getItemById(id));
this.tab = params.get('tab') || 'list';
const id = +params.get('id');
- if (id && id !== this.itemId) {
- this.itemId = id;
+ if (id && id !== this.currentItemId) {
+ this.currentItemId = id;
this.getItemById(id);
}
});
}
tabChange(evt: NgbNavChangeEvent) {
- this.router.navigate([`/staff/cat/item/${this.itemId}/${evt.nextId}`]);
+ this.router.navigate([`/staff/cat/item/${this.currentItemId}/${evt.nextId}`]);
}
- getItemByBarcode(): Promise<any> {
- this.itemId = null;
+ getItemFromBarcodeInput(): Promise<any> {
+ this.currentItemId = null;
this.item = null;
if (!this.itemBarcode) { return Promise.resolve(); }
- return this.barcodeSelect.getBarcode('asset', this.itemBarcode)
+ // The barcode may be a comma-separated list of values.
+ const barcodes = [];
+ this.itemBarcode.split(/,/).forEach(bc => {
+ bc = bc.replace(/[\s,]+/g,'');
+ if (bc) { barcodes.push(bc); }
+ });
+
+ 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}`]);
+ }
+ })
+ );
+ })).toPromise();
+ }
+
+ getOneItemFromBarcode(barcode: string): Promise<any> {
+ return this.barcodeSelect.getBarcode('asset', barcode)
.then(res => {
if (!res.id) {
this.noSuchItem = true;
} else {
this.itemBarcode = null;
-
if (this.tab === 'list') {
this.selectInput();
return this.getItemById(res.id);
- } else {
- this.router.navigate([`/staff/cat/item/${res.id}/${this.tab}`]);
}
}
});
this.item = item;
this.mungeIsbns();
this.selectInput();
- this.itemService.scannedItems.unshift(item);
- this.grid.reload();
+
+ 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);
+ }
+ }
+
+ return this.getItemCirc(item).then(_ => {
+ if (this.grid) {
+ this.grid.reload();
+ }
+ });
});
}
+ getItemCirc(item: IdlObject): Promise<any> {
+ if (item.status().id() !== 1 /* Checked Out */) {
+ return Promise.resolve();
+ }
+
+ return this.pcrud.search('circ', {target_copy: item.id()},
+ {order_by: {circ: 'xact_start DESC'}, limit: 1})
+ .toPromise().then(circ => item._circ = circ);
+ }
+
// A bit of cleanup to make the ISBN's look friendlier
mungeIsbns() {
const item = this.item;
.then(success => success ? this.load() : null);
}
- showDetails() {
+ showDetails(copy?: IdlObject) {
+ let copyId;
+
+ if (copy) {
+ // Row doubleclick
+ copyId = copy.id();
+ } else if (this.grid) {
+ // 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();
+ }
+
+ this.router.navigate([`/staff/cat/item/${copyId}/summary`]);
}
showList() {
+ this.currentItemId = null;
this.router.navigate(['/staff/cat/item/list']);
}
}