LPXXX Item Status Angular WIP user/berick/lpxxx-item-status-angular
authorBill Erickson <berickxx@gmail.com>
Tue, 29 Jun 2021 21:54:13 +0000 (17:54 -0400)
committerBill Erickson <berickxx@gmail.com>
Tue, 29 Jun 2021 21:54:13 +0000 (17:54 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Open-ILS/src/eg2/src/app/staff/cat/item/item.module.ts
Open-ILS/src/eg2/src/app/staff/cat/item/recent-history.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/cat/item/recent-history.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/cat/item/status.component.html
Open-ILS/src/eg2/src/app/staff/cat/item/summary.component.html
Open-ILS/src/eg2/src/app/staff/cat/item/summary.component.ts
Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts

index a9e31b1..f104689 100644 (file)
@@ -9,12 +9,14 @@ 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,
     ItemSummaryComponent,
-    ItemStatusComponent
+    ItemStatusComponent,
+    ItemRecentHistoryComponent
   ],
   imports: [
     StaffCommonModule,
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/item/recent-history.component.html b/Open-ILS/src/eg2/src/app/staff/cat/item/recent-history.component.html
new file mode 100644 (file)
index 0000000..ddf0cee
--- /dev/null
@@ -0,0 +1,62 @@
+
+<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.prevCircSummary">
+            {{circInfo.prevCircSummary.last_renewal_time() | date:format.dateTimeFormat}}
+          </ng-container>
+        </div>
+      </div>
+
+    </div>
+  </div>
+</div>
+
diff --git a/Open-ILS/src/eg2/src/app/staff/cat/item/recent-history.component.ts b/Open-ILS/src/eg2/src/app/staff/cat/item/recent-history.component.ts
new file mode 100644 (file)
index 0000000..210f000
--- /dev/null
@@ -0,0 +1,63 @@
+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);
+    }
+}
+
+
index d383d5a..ea75b82 100644 (file)
         <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>
 
index f688317..012ef2a 100644 (file)
@@ -47,8 +47,8 @@
 
     <div class="well-label" i18n>Due Date</div>
     <div class="well-value">
-      <ng-container *ngIf="circ">
-        {{circ.due_date() | date:'shortDate'}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        {{circInfo.currentCirc.due_date() | date:'shortDate'}}
       </ng-container>
     </div>
   </div>
@@ -71,8 +71,8 @@
 
     <div class="well-label" i18n>Checkout Date</div>
     <div class="well-value">
-      <ng-container *ngIf="circ">
-        {{circSummary.start_time() | date:'shortDate'}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        {{circInfo.circSummary.start_time() | date:'shortDate'}}
       </ng-container>
     </div>
   </div>
 
     <div class="well-label" i18n>Renewal Type</div>
     <div class="well-value">
-      <ng-container *ngIf="circ">
-        <div *ngIf="circ.opac_renewal() == 't'" i18n>OPAC</div>
-        <div *ngIf="circ.desk_renewal() == 't'" i18n>Desk</div>
-        <div *ngIf="circ.phone_renewal() == 't'" i18n>Phone</div>
-        <div *ngIf="circ.auto_renewal() == 't'" i18n>Automatic</div>
+      <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="circ">
-        {{circSummary.checkout_workstation()}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        {{circInfo.circSummary.checkout_workstation()}}
       </ng-container>
     </div>
   </div>
     </div>
 
     <div class="well-label" i18n>Total Circs</div>
-    <div class="well-value">{{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="circ">
-        {{circ.duration_rule().name()}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        {{circInfo.currentCirc.duration_rule().name()}}
       </ng-container>
     </div>
   </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">{{total_circs_this_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="circ">
-        {{circ.recurring_fine_rule().name()}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        {{circInfo.currentCirc.recurring_fine_rule().name()}}
       </ng-container>
     </div>
   </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">{{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="circ">
-        {{circ.max_fine_rule().name()}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        {{circInfo.currentCirc.max_fine_rule().name()}}
       </ng-container>
     </div>
   </div>
 
     <div class="well-label" i18n>Checkin Time</div>
     <div class="well-value">
-      <ng-container *ngIf="circ">
-        {{circ.checkin_time() || 
-          circSummary.last_checkin_time() | date:'shortDate'}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        {{circInfo.currentCirc.checkin_time() || 
+          circInfo.circSummary.last_checkin_time() | date:'shortDate'}}
       </ng-container>
     </div>
   </div>
 
     <div class="well-label" i18n>Renewal Workstation</div>
     <div class="well-value">
-      <ng-container *ngIf="circ">
-        {{circSummary.last_renewal_workstation()}}
+      <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="circ">
-        {{circ.renewal_remaining()}}
+      <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="circ">
-        {{circ.checkin_scan_time() || 
-          circSummary.last_checkin_scan_time() | date:'shortDate'}}
+      <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-label" i18n>Checkin Workstation</div>
     <div class="well-value">
-      <ng-container *ngIf="circ">
-        <ng-container *ngIf="circ.checkin_workstation()">
-          {{circ.checkin_workstation().name()}}
+      <ng-container *ngIf="circInfo.currentCirc">
+        <ng-container *ngIf="circInfo.currentCirc.checkin_workstation()">
+          {{circInfo.currentCirc.checkin_workstation().name()}}
         </ng-container>
         <ng-container 
-          *ngIf="!circ.checkin_workstation() && circSummary.last_checkin_workstation()">
-          {{circSummary.last_checkin_workstation().name()}}
+          *ngIf="!circInfo.currentCirc.checkin_workstation() && circInfo.circSummary.last_checkin_workstation()">
+          {{circInfo.circSummary.last_checkin_workstation().name()}}
         </ng-container>
       </ng-container>
     </div>
     <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" [disabled]="item.copy_alerts().length == 0"
+      <button class="btn btn-outline-dark ml-2" [disabled]="item.copy_alerts().length == 0"
         (click)="manageItemAlerts()"  i18n>Manage</button>
     </div>
 
index 721c803..3b02cbf 100644 (file)
@@ -12,7 +12,7 @@ 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} from '@eg/staff/share/circ/circ.service';
+import {CircService, ItemCircInfo} from '@eg/staff/share/circ/circ.service';
 import {CopyAlertsDialogComponent
     } from '@eg/staff/share/holdings/copy-alerts-dialog.component';
 
@@ -25,12 +25,8 @@ export class ItemSummaryComponent implements OnInit {
 
     @Input() item: IdlObject;
 
-    circ: IdlObject;
-    circSummary: IdlObject;
-    prevCircSummary: IdlObject;
-    prevCircUser: IdlObject;
-    maxHistoryCount: number;
     loading = false;
+    circInfo: ItemCircInfo;
 
     @ViewChild('copyAlertsDialog') private copyAlertsDialog: CopyAlertsDialogComponent;
 
@@ -57,53 +53,10 @@ export class ItemSummaryComponent implements OnInit {
     }
 
     loadCircInfo(): Promise<any> {
-
-        const copyOrg =
-            this.item.call_number().id() === -1 ?
-            this.item.circ_lib().id() :
-            this.item.call_number().owning_lib().id();
-
-        return this.perms.hasWorkPermAt(['VIEW_COPY_CHECKOUT_HISTORY'], copyOrg)
-        .then(hasPerm => {
-            if (hasPerm) {
-                return this.org.settings('circ.item_checkout_history.max')
-                .then(sets => {
-                    this.maxHistoryCount = sets['circ.item_checkout_history.max'] || 4;
-                });
-            }
-        })
-
-        .then(_ => this.circs.getLatestCirc(this.item.id()))
-
-        .then(circ => {
-            this.circ = circ;
-
-            if (!circ) { return Promise.resolve(); }
-
-            return this.circs.getCircChain(this.circ.id())
-            .then(summary => {
-                this.circSummary = summary;
-
-                if (this.maxHistoryCount <= 1) { return; }
-
-                return this.circs.getPrevCircChain(this.circ.id())
-                .then(prevSummary => {
-                    if (!prevSummary) { return; }
-
-                    this.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 => this.prevCircUser = user);
-                    }
-                });
-            });
-        });
+        return this.circs.getItemCircInfo(this.item)
+        .then(info => this.circInfo = info);
     }
 
-
     addItemAlerts() {
         this.copyAlertsDialog.copyIds = [this.item.id()];
         this.copyAlertsDialog.mode = 'create';
index ee56fed..81bee26 100644 (file)
@@ -15,6 +15,7 @@ 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 '@eg/staff/share/worklog/worklog.service';
+import {PermService} from '@eg/core/perm.service';
 
 export interface CircDisplayInfo {
     title?: string;
@@ -196,6 +197,17 @@ export interface CheckinResult extends CircResultCommon {
     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;
@@ -220,6 +232,7 @@ export class CircService {
         private auth: AuthService,
         private holdings: HoldingsService,
         private worklog: WorkLogService,
+        private perms: PermService,
         private bib: BibRecordService
     ) {}
 
@@ -1271,5 +1284,83 @@ export class CircService {
 
         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);
+                    }
+                });
+            });
+        });
+    }
 }
 
+