import {HoldingsModule} from '@eg/staff/share/holdings/holdings.module';
import {PatronModule} from '@eg/staff/share/patron/patron.module';
import {MarkItemMissingPiecesComponent} from './missing-pieces.component';
+import {ItemStatusComponent} from './status.component';
+import {BarcodesModule} from '@eg/staff/share/barcodes/barcodes.module';
+import {CircModule} from '@eg/staff/share/circ/circ.module';
+import {ItemSummaryComponent} from './summary.component';
+import {ItemRecentHistoryComponent} from './recent-history.component';
@NgModule({
declarations: [
- MarkItemMissingPiecesComponent
+ MarkItemMissingPiecesComponent,
+ ItemSummaryComponent,
+ ItemStatusComponent,
+ ItemRecentHistoryComponent
],
imports: [
StaffCommonModule,
CommonWidgetsModule,
ItemRoutingModule,
HoldingsModule,
+ BarcodesModule,
+ CircModule,
PatronModule
],
providers: [
--- /dev/null
+
+<div class="row pt-3" *ngIf="circInfo">
+ <div class="col-lg-6">
+ <div *ngIf="!circInfo.prevCircSummary" class="alert alert-info" i18n>
+ No Previous Circ Group
+ </div>
+ <div class="well-table" *ngIf="item && !loading && circInfo.prevCircSummary">
+ </div>
+ </div>
+ <div class="col-lg-6">
+ <div *ngIf="!circInfo.circSummary" class="alert alert-info" i18n>
+ No Recent Circ Group
+ </div>
+ <div class="well-table" *ngIf="item && !loading && circInfo.currentCirc">
+ <div class="well-row">
+ <div class="well-label" i18n>Patron</div>
+ <div class="well-value" *ngIf="circInfo.currentCirc">
+ <a i18n
+ routerLink="/staff/circInfo.currentCirc/patron/{{circInfo.currentCirc.usr().id()}}">
+ {{circInfo.currentCirc.usr().family_name()}},
+ {{circInfo.currentCirc.usr().first_given_name()}},
+ {{circInfo.currentCirc.usr().second_given_name()}}
+ </a>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Total Circs</div>
+ <div class="well-value">{{circInfo.totalCircs}}</div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Checkout Date</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.circSummary.start_time() | date:format.dateTimeFormat}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Checkout Workstation</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.circSummary.checkout_workstation()}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Last Renewed On</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.circSummary">
+ {{circInfo.circSummary.last_renewal_time() | date:format.dateTimeFormat}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Renewal Workstation</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.circSummary.last_renewal_workstation()}}
+ </ng-container>
+ </div>
+ </div>
+
+ </div>
+ </div>
+</div>
+
--- /dev/null
+import {Component, Input, OnInit, AfterViewInit, ViewChild} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {OrgService} from '@eg/core/org.service';
+import {PrintService} from '@eg/share/print/print.service';
+import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
+import {EventService} from '@eg/core/event.service';
+import {PermService} from '@eg/core/perm.service';
+import {PatronPenaltyDialogComponent} from '@eg/staff/share/patron/penalty-dialog.component';
+import {BarcodeSelectComponent} from '@eg/staff/share/barcodes/barcode-select.component';
+import {CatalogService} from '@eg/share/catalog/catalog.service';
+import {CircService, ItemCircInfo} from '@eg/staff/share/circ/circ.service';
+import {CopyAlertsDialogComponent
+ } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
+import {FormatService} from '@eg/core/format.service';
+
+@Component({
+ selector: 'eg-item-recent-history',
+ templateUrl: 'recent-history.component.html'
+})
+
+export class ItemRecentHistoryComponent implements OnInit {
+
+ @Input() item: IdlObject;
+
+ loading = false;
+ circInfo: ItemCircInfo;
+
+ @ViewChild('copyAlertsDialog') private copyAlertsDialog: CopyAlertsDialogComponent;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private org: OrgService,
+ private printer: PrintService,
+ private pcrud: PcrudService,
+ private auth: AuthService,
+ private perms: PermService,
+ private idl: IdlService,
+ private evt: EventService,
+ private cat: CatalogService,
+ private holdings: HoldingsService,
+ private circs: CircService,
+ public format: FormatService
+ ) { }
+
+ ngOnInit() {
+ this.loading = true;
+ this.loadCircInfo()
+ .then(_ => this.loading = false);
+ }
+
+ loadCircInfo(): Promise<any> {
+ return this.circs.getItemCircInfo(this.item)
+ .then(info => this.circInfo = info);
+ }
+}
+
+
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {MarkItemMissingPiecesComponent} from './missing-pieces.component';
+import {ItemStatusComponent} from './status.component';
const routes: Routes = [{
path: 'missing_pieces',
}, {
path: 'missing_pieces/:id',
component: MarkItemMissingPiecesComponent
+ }, {
+ path: 'list',
+ component: ItemStatusComponent
+ }, {
+ path: ':id/:tab',
+ component: ItemStatusComponent
+ }, {
+ path: ':id',
+ component: ItemStatusComponent
}];
@NgModule({
--- /dev/null
+<eg-staff-banner i18n-bannerText bannerText="Item Status">
+</eg-staff-banner>
+
+<eg-barcode-select #barcodeSelect></eg-barcode-select>
+<eg-circ-components></eg-circ-components>
+<eg-bucket-dialog #bucketDialog></eg-bucket-dialog>
+
+<div class="row">
+ <div class="col-lg-12 form-inline">
+ <div class="input-group">
+ <div class="input-group-prepend">
+ <span class="input-group-text" id='barcode-label' i18n>Barcode</span>
+ </div>
+ <input type="text" class="form-control" id="item-barcode-input"
+ (keydown)="noSuchItem=false; true;"
+ (keyup.enter)="getItemByBarcode()" [(ngModel)]="itemBarcode"
+ aria-describedby="barcode-label"/>
+ </div>
+ <button class="btn btn-outline-dark"
+ (click)="getItemByBarcode()" i18n>Submit</button>
+ </div>
+</div>
+
+
+
+<div *ngIf="tab != 'list' && item">
+
+ <div class="mt-2 mb-4">
+ <eg-bib-summary [recordId]="item.call_number().record().id()">
+ </eg-bib-summary>
+ </div>
+
+ <ul ngbNav #itemNav="ngbNav" class="nav-tabs"
+ [activeId]="tab" (navChange)="tabChange($event)">
+ <li ngbNavItem="summary">
+ <a ngbNavLink i18n>Quick Summary</a>
+ <ng-template ngbNavContent>
+ <eg-item-summary [item]="item"></eg-item-summary>
+ </ng-template>
+ </li>
+ <li ngbNavItem="recent-history">
+ <a ngbNavLink i18n>Recent Circ History</a>
+ <ng-template ngbNavContent>
+ <eg-item-recent-history [item]="item"></eg-item-recent-history>
+ </ng-template>
+ </li>
+ </ul>
+ <div [ngbNavOutlet]="itemNav"></div>
+
+</div>
+
--- /dev/null
+import {Component, Input, OnInit, AfterViewInit, ViewChild} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {PrintService} from '@eg/share/print/print.service';
+import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
+import {EventService} from '@eg/core/event.service';
+import {PatronPenaltyDialogComponent} from '@eg/staff/share/patron/penalty-dialog.component';
+import {BarcodeSelectComponent} from '@eg/staff/share/barcodes/barcode-select.component';
+import {CatalogService} from '@eg/share/catalog/catalog.service';
+import {NgbNav, NgbNavChangeEvent} from '@ng-bootstrap/ng-bootstrap';
+import {CopyAlertsDialogComponent
+ } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
+import {BucketDialogComponent
+ } from '@eg/staff/share/buckets/bucket-dialog.component';
+
+@Component({
+ templateUrl: 'status.component.html'
+})
+
+export class ItemStatusComponent implements OnInit, AfterViewInit {
+
+ itemId: number;
+ itemBarcode: string;
+ noSuchItem = false;
+ item: IdlObject;
+ tab: string;
+
+ @ViewChild('barcodeSelect') private barcodeSelect: BarcodeSelectComponent;
+ @ViewChild('bucketDialog') private bucketDialog: BucketDialogComponent;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private printer: PrintService,
+ private pcrud: PcrudService,
+ private auth: AuthService,
+ private evt: EventService,
+ private cat: CatalogService,
+ private holdings: HoldingsService
+ ) {}
+
+ ngOnInit() {
+
+ this.itemId = +this.route.snapshot.paramMap.get('id');
+ this.tab = this.route.snapshot.paramMap.get('tab');
+
+ if (!this.tab) {
+ if (this.itemId) {
+ this.router.navigate([`/staff/cat/item/${this.itemId}/summary`])
+ .then(ok => {if (ok) { this.load(); }});
+ return;
+ } else {
+ this.tab = 'list';
+ }
+ }
+
+ this.load();
+ }
+
+ load() {
+
+ this.cat.fetchCcvms()
+ .then(_ => this.cat.fetchCmfs())
+ .then(_ => {
+ if (this.itemId) {
+ return this.getItemById(this.itemId);
+ }
+ })
+ .then(_ => {
+ // Avoid watching for changes until after ngOnInit is complete
+ // so we don't grab the same copy twice.
+
+ this.route.paramMap.subscribe((params: ParamMap) => {
+ this.tab = params.get('tab');
+ const id = +params.get('id');
+
+ if (id !== this.itemId) {
+ this.itemId = id;
+ if (id) {
+ this.getItemById(id);
+ }
+ }
+ });
+ });
+ }
+
+ ngAfterViewInit() {
+ this.selectInput();
+ }
+
+ tabChange(evt: NgbNavChangeEvent) {
+ this.router.navigate([`/staff/cat/item/${this.itemId}/${evt.nextId}`]);
+ }
+
+ getItemByBarcode(): Promise<any> {
+ this.itemId = null;
+ this.item = null;
+
+ if (!this.itemBarcode) { return Promise.resolve(); }
+
+ return this.barcodeSelect.getBarcode('asset', this.itemBarcode)
+ .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}`]);
+ }
+ }
+ });
+ }
+
+ selectInput() {
+ setTimeout(() => {
+ const node: HTMLInputElement =
+ document.getElementById('item-barcode-input') as HTMLInputElement;
+ if (node) { node.select(); }
+ });
+ }
+
+ getItemById(id: number): Promise<any> {
+
+ const flesh = {
+ flesh : 4,
+ flesh_fields : {
+ acp : [
+ 'call_number', 'location', 'status', 'floating', 'circ_modifier',
+ 'age_protect', 'circ_lib', 'copy_alerts', 'creator',
+ 'editor', 'circ_as_type', 'latest_inventory', 'floating'
+ ],
+ acn : ['record', 'prefix', 'suffix', 'label_class', 'owning_lib'],
+ bre : ['simple_record', 'creator', 'editor'],
+ alci : ['inventory_workstation']
+ },
+ select : {
+ // avoid fleshing MARC on the bre
+ // note: don't add simple_record.. not sure why
+ bre : ['id', 'tcn_value', 'creator', 'editor', 'create_date', 'edit_date'],
+ }
+ };
+
+ return this.pcrud.retrieve('acp', id, flesh)
+ .toPromise().then(item => {
+ this.item = item;
+ this.itemId = item.id();
+ this.selectInput();
+ });
+ }
+}
+
+
--- /dev/null
+
+<eg-copy-alerts-dialog #copyAlertsDialog></eg-copy-alerts-dialog>
+
+<div class="alert alert-danger" *ngIf="item.deleted() == 't'">
+ This item has been marked as Deleted.
+</div>
+
+<div class="well-table" *ngIf="item && !loading">
+
+ <div class="well-row" *ngIf="item.dummy_title() || item.dummy_author()">
+ <div class="well-label" *ngIf="item.dummy_title()" i18n>Precat Title</div>
+ <div class="well-value" *ngIf="item.dummy_title()">{{item.dummy_title()}}</div>
+
+ <div class="well-label" *ngIf="item.dummy_author()" i18n>Precat Author</div>
+ <div class="well-value" *ngIf="item.dummy_author()">{{item.dummy_author()}}</div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Barcode</div>
+ <div class="well-value">{{item.barcode()}}</div>
+
+ <div class="well-label" i18n>Circ Library</div>
+ <div class="well-value">{{item.circ_lib().shortname()}}</div>
+
+ <div class="well-label" i18n>Call # Prefix</div>
+ <div class="well-value">
+ {{item.call_number().prefix().label()}}
+ </div>
+
+ <div class="well-label" i18n>Status</div>
+ <div
+ class="well-value"
+ title="Holdable: {{item.status().holdable()}} /
+ OPAC Visible: {{item.status().opac_visible()}}"
+ >{{item.status().name()}}</div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Price</div>
+ <div class="well-value">{{item.price()}}</div>
+
+ <div class="well-label" i18n>Owning Library</div>
+ <div class="well-value">{{item.call_number().owning_lib().shortname()}}</div>
+
+ <div class="well-label" i18n>Call #</div>
+ <div class="well-value">{{item.call_number().label()}}</div>
+
+ <div class="well-label" i18n>Due Date</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.currentCirc | egDueDatePipe}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Acquisition Cost</div>
+ <div class="well-value">{{item.cost()}}</div>
+
+ <div class="well-label" i18n>Shelving Location</div>
+ <div
+ class="well-value"
+ title="Holdable: {{item.location().holdable()}} /
+ OPAC Visible: {{item.location().opac_visible()}}"
+ >{{item.location().name()}}</div>
+
+ <div class="well-label" i18n>Call # Suffix</div>
+ <div class="well-value">
+ {{item.call_number().suffix().label()}}
+ </div>
+
+ <div class="well-label" i18n>Checkout Date</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.circSummary.start_time() | date:'shortDate'}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>ISBN</div>
+ <div class="well-value">
+ {{item.call_number().record().simple_record().isbn() || item.dummy_isbn()}}
+ </div>
+
+ <div class="well-label" i18n>Loan Duration</div>
+ <div class="well-value">
+ <div *ngIf="item.loan_duration() == 1" i18n>Short</div>
+ <div *ngIf="item.loan_duration() == 2" i18n>Normal</div>
+ <div *ngIf="item.loan_duration() == 3" i18n>Long</div>
+ </div>
+
+ <div class="well-label" i18n>Renewal Type</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ <div *ngIf="circInfo.currentCirc.opac_renewal() == 't'" i18n>OPAC</div>
+ <div *ngIf="circInfo.currentCirc.desk_renewal() == 't'" i18n>Desk</div>
+ <div *ngIf="circInfo.currentCirc.phone_renewal() == 't'" i18n>Phone</div>
+ <div *ngIf="circInfo.currentCirc.auto_renewal() == 't'" i18n>Automatic</div>
+ </ng-container>
+ </div>
+
+ <div class="well-label" i18n>Checkout Workstation</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.circSummary.checkout_workstation()}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Date Created</div>
+ <div class="well-value">{{item.create_date() | date:'shortDate'}}</div>
+ <div class="well-label" i18n>Fine Level</div>
+ <div class="well-value">
+ <div *ngIf="item.fine_level() == 1" i18n>Low</div>
+ <div *ngIf="item.fine_level() == 2" i18n>Normal</div>
+ <div *ngIf="item.fine_level() == 3" i18n>High</div>
+ </div>
+
+ <div class="well-label" i18n>Total Circs</div>
+ <div class="well-value">{{circInfo.totalCircs}}</div>
+
+ <div class="well-label" i18n>Duration Rule</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.currentCirc.duration_rule().name()}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Date Active</div>
+ <div class="well-value">{{item.active_date() | date:'shortDate'}}</div>
+
+ <div class="well-label" i18n>Reference</div>
+ <div class="well-value"><eg-bool [value]="item.ref()"></eg-bool></div>
+
+ <div class="well-label" i18n>Total Circs - Current Year</div>
+ <div class="well-value">{{circInfo.circsThisYear}}</div>
+
+ <div class="well-label" i18n>Recurring Fine Rule</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.currentCirc.recurring_fine_rule().name()}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Status Changed</div>
+ <div class="well-value">{{item.status_changed_time() | date:'shortDate'}}</div>
+
+ <div class="well-label" i18n>OPAC Visible</div>
+ <div class="well-value"><eg-bool [value]="item.opac_visible()"></eg-bool></div>
+
+ <div class="well-label" i18n>Total Circs - Prev Year</div>
+ <div class="well-value">{{circInfo.circsPrevYear}}</div>
+
+ <div class="well-label" i18n>Max Fine Rule</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.currentCirc.max_fine_rule().name()}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Item ID</div>
+ <div class="well-value">{{item.id()}}</div>
+
+ <div class="well-label" i18n>Holdable</div>
+ <div class="well-value"><eg-bool [value]="item.holdable()"></eg-bool></div>
+
+ <div class="well-label" i18n>In-House Uses</div>
+ <div class="well-value">{{item._inHouseUseCount}}</div>
+
+ <div class="well-label" i18n>Checkin Time</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.currentCirc.checkin_time() ||
+ circInfo.circSummary.last_checkin_time() | date:'shortDate'}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Circulate</div>
+ <div class="well-value"><eg-bool [value]="item.circulate()"></eg-bool></div>
+
+ <div class="well-label" i18n>Renewal Workstation</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.circSummary.last_renewal_workstation()}}
+ </ng-container>
+ </div>
+
+ <div class="well-label" i18n>Remaining Renewals</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.currentCirc.renewal_remaining()}}
+ </ng-container>
+ </div>
+
+ <div class="well-label" i18n>Checkin Scan Time</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ {{circInfo.currentCirc.checkin_scan_time() ||
+ circInfo.circSummary.last_checkin_scan_time() | date:'shortDate'}}
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Floating</div>
+ <div class="well-value">
+ <ng-container *ngIf="item.floating()">
+ {{item.floating().name()}}
+ </ng-container>
+ </div>
+
+ <div class="well-label" i18n>Circ Modifier</div>
+ <div class="well-value">
+ <ng-container *ngIf="item.circ_modifier()">
+ {{item.circ_modifier().name()}}
+ </ng-container>
+ </div>
+
+ <div class="well-label" i18n>Age-based Hold Protection</div>
+ <div class="well-value">
+ <ng-container *ngIf="item.age_protect()">
+ {{item.age_protect().name()}}
+ </ng-container>
+ </div>
+
+ <div class="well-label" i18n>Checkin Workstation</div>
+ <div class="well-value">
+ <ng-container *ngIf="circInfo.currentCirc">
+ <ng-container *ngIf="circInfo.currentCirc.checkin_workstation()">
+ {{circInfo.currentCirc.checkin_workstation().name()}}
+ </ng-container>
+ <ng-container
+ *ngIf="!circInfo.currentCirc.checkin_workstation() && circInfo.circSummary.last_checkin_workstation()">
+ {{circInfo.circSummary.last_checkin_workstation().name()}}
+ </ng-container>
+ </ng-container>
+ </div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Inventory Date</div>
+ <div class="well-value">
+ <ng-container *ngIf="item.latest_inventory()">
+ {{item.latest_inventory().inventory_date() | date:'shortDate'}}
+ </ng-container>
+ </div>
+
+ <div class="well-label" i18n>Inventory Workstation</div>
+ <div class="well-value">
+ <ng-container *ngIf="item.latest_inventory()">
+ {{item.latest_inventory().inventory_workstation().name()}}
+ </ng-container>
+ </div>
+
+ <div class="well-label"></div>
+ <div class="well-value"></div>
+ <div class="well-label"></div>
+ <div class="well-value"></div>
+ </div>
+
+ <div class="well-row">
+ <div class="well-label" i18n>Item Alerts</div>
+ <div class="well-value" id="item-status-alert-msg">
+ <button class="btn btn-outline-dark" (click)="addItemAlerts()" i18n>Add</button>
+ <button class="btn btn-outline-dark ml-2" [disabled]="item.copy_alerts().length == 0"
+ (click)="manageItemAlerts()" i18n>Manage</button>
+ </div>
+
+ <!-- maintain positioning -->
+ <div class="well-label"></div>
+ <div class="well-value"></div>
+ <div class="well-label"></div>
+ <div class="well-value"></div>
+ <div class="well-label"></div>
+ <div class="well-value"></div>
+ </div>
+
+</div>
--- /dev/null
+import {Component, Input, OnInit, AfterViewInit, ViewChild} from '@angular/core';
+import {Router, ActivatedRoute, ParamMap} from '@angular/router';
+import {IdlService, IdlObject} from '@eg/core/idl.service';
+import {PcrudService} from '@eg/core/pcrud.service';
+import {AuthService} from '@eg/core/auth.service';
+import {NetService} from '@eg/core/net.service';
+import {OrgService} from '@eg/core/org.service';
+import {PrintService} from '@eg/share/print/print.service';
+import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
+import {EventService} from '@eg/core/event.service';
+import {PermService} from '@eg/core/perm.service';
+import {PatronPenaltyDialogComponent} from '@eg/staff/share/patron/penalty-dialog.component';
+import {BarcodeSelectComponent} from '@eg/staff/share/barcodes/barcode-select.component';
+import {CatalogService} from '@eg/share/catalog/catalog.service';
+import {CircService, ItemCircInfo} from '@eg/staff/share/circ/circ.service';
+import {CopyAlertsDialogComponent
+ } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
+
+@Component({
+ selector: 'eg-item-summary',
+ templateUrl: 'summary.component.html'
+})
+
+export class ItemSummaryComponent implements OnInit {
+
+ @Input() item: IdlObject;
+
+ loading = false;
+ circInfo: ItemCircInfo;
+
+ @ViewChild('copyAlertsDialog') private copyAlertsDialog: CopyAlertsDialogComponent;
+
+ constructor(
+ private router: Router,
+ private route: ActivatedRoute,
+ private net: NetService,
+ private org: OrgService,
+ private printer: PrintService,
+ private pcrud: PcrudService,
+ private auth: AuthService,
+ private perms: PermService,
+ private idl: IdlService,
+ private evt: EventService,
+ private cat: CatalogService,
+ private holdings: HoldingsService,
+ private circs: CircService
+ ) { }
+
+ ngOnInit() {
+ this.loading = true;
+ this.loadCircInfo()
+ .then(_ => this.loading = false);
+ }
+
+ loadCircInfo(): Promise<any> {
+ return this.circs.getItemCircInfo(this.item)
+ .then(info => this.circInfo = info);
+ }
+
+ addItemAlerts() {
+ this.copyAlertsDialog.copyIds = [this.item.id()];
+ this.copyAlertsDialog.mode = 'create';
+ this.copyAlertsDialog.open({size: 'lg'}).subscribe();
+ }
+
+ manageItemAlerts() {
+ this.copyAlertsDialog.copyIds = [this.item.id()];
+ this.copyAlertsDialog.mode = 'manage';
+ this.copyAlertsDialog.open({size: 'lg'}).subscribe();
+ }
+}
+
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 {PcrudService, PcrudQueryOps} 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 {ServerStoreService} from '@eg/core/server-store.service';
import {HoldingsService} from '@eg/staff/share/holdings/holdings.service';
import {WorkLogService, WorkLogEntry} from '@eg/staff/share/worklog/worklog.service';
+import {PermService} from '@eg/core/perm.service';
export interface CircDisplayInfo {
title?: string;
destCourierCode?: string;
}
+export interface ItemCircInfo {
+ maxHistoryCount: number;
+ circSummary?: IdlObject;
+ prevCircSummary?: IdlObject;
+ currentCirc?: IdlObject;
+ prevCircUser?: IdlObject;
+ totalCircs: number;
+ circsThisYear: number;
+ circsPrevYear: number;
+}
+
@Injectable()
export class CircService {
static resultIndex = 0;
private auth: AuthService,
private holdings: HoldingsService,
private worklog: WorkLogService,
+ private perms: PermService,
private bib: BibRecordService
) {}
return checkDigit;
}
+
+ getCircChain(circId: number): Promise<IdlObject> {
+ return this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.renewal_chain.retrieve_by_circ.summary',
+ this.auth.token(), circId
+ ).toPromise();
+ }
+
+ getPrevCircChain(circId: number): Promise<IdlObject> {
+
+ return this.net.request(
+ 'open-ils.circ',
+ 'open-ils.circ.prev_renewal_chain.retrieve_by_circ.summary',
+ this.auth.token(), circId
+
+ ).toPromise();
+ }
+
+ getLatestCirc(copyId: number, ops?: PcrudQueryOps): Promise<IdlObject> {
+
+ if (!ops) {
+ ops = {
+ flesh: 2,
+ flesh_fields: {
+ aacs: [
+ 'usr',
+ 'workstation',
+ 'checkin_workstation',
+ 'duration_rule',
+ 'max_fine_rule',
+ 'recurring_fine_rule'
+ ],
+ au: ['card']
+ }
+ };
+ }
+
+ ops.order_by = {aacs: 'xact_start desc'};
+ ops.limit = 1;
+
+ return this.pcrud.search('aacs', {target_copy : copyId}, ops).toPromise();
+ }
+
+ getItemCircInfo(item: IdlObject): Promise<ItemCircInfo> {
+
+ const response: ItemCircInfo = {
+ maxHistoryCount: 0,
+ totalCircs: 0,
+ circsThisYear: 0,
+ circsPrevYear: 0
+ };
+
+ const copyOrg: number =
+ item.call_number().id() === -1 ?
+ item.circ_lib().id() :
+ item.call_number().owning_lib().id();
+
+ return this.pcrud.search('circbyyr',
+ {copy : item.id()}, null, {atomic : true}).toPromise()
+
+ .then(counts => {
+
+ const curYear = new Date().getFullYear();
+ const prevYear = curYear - 1;
+
+ counts.forEach(c => {
+ response.totalCircs += Number(c.count());
+ if (c.year() === curYear) {
+ response.circsThisYear += Number(c.count());
+ }
+ if (c.year() === prevYear) {
+ response.circsPrevYear += Number(c.count());
+ }
+ });
+ })
+ .then(_ => this.perms.hasWorkPermAt(['VIEW_COPY_CHECKOUT_HISTORY'], true))
+ .then(hasPerm => {
+ if (hasPerm['VIEW_COPY_CHECKOUT_HISTORY'].includes(copyOrg)) {
+ return this.org.settings('circ.item_checkout_history.max')
+ .then(sets => {
+ response.maxHistoryCount = sets['circ.item_checkout_history.max'] || 4;
+ });
+ } else {
+ response.maxHistoryCount = 0;
+ }
+ })
+
+ .then(_ => this.getLatestCirc(item.id()))
+
+ .then(circ => {
+
+ if (!circ) { return response; }
+
+ response.currentCirc = circ;
+
+ return this.getCircChain(circ.id())
+ .then(summary => {
+ response.circSummary = summary;
+
+ if (response.maxHistoryCount <= 1) {
+ return response;
+ }
+
+ return this.getPrevCircChain(circ.id())
+ .then(prevSummary => {
+ if (!prevSummary) { return response; }
+
+ response.prevCircSummary = prevSummary.summary;
+
+ if (prevSummary.usr) { // aged circs have no 'usr'.
+
+ return this.pcrud.retrieve('au', prevSummary.usr,
+ {flesh : 1, flesh_fields : {au : ['card']}})
+ .toPromise().then(user => response.prevCircUser = user);
+ }
+ });
+ });
+ });
+ }
}
+