LP1904036 precat dialog
authorBill Erickson <berickxx@gmail.com>
Mon, 8 Feb 2021 18:15:08 +0000 (13:15 -0500)
committerGalen Charlton <gmc@equinoxOLI.org>
Fri, 28 Oct 2022 00:13:23 +0000 (20:13 -0400)
Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.html
Open-ILS/src/eg2/src/app/staff/circ/patron/checkout.component.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.module.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/patron.service.ts
Open-ILS/src/eg2/src/app/staff/circ/patron/precat-dialog.component.html [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/circ/patron/precat-dialog.component.ts [new file with mode: 0644]
Open-ILS/src/eg2/src/app/staff/share/circ/circ.service.ts

index d2a7615..665034a 100644 (file)
@@ -1,6 +1,9 @@
 
 <eg-progress-dialog #progressDialog></eg-progress-dialog>
 
+<eg-precat-checkout-dialog #precatDialog [barcode]="checkoutBarcode">
+</eg-precat-checkout-dialog>
+
 <eg-prompt-dialog #nonCatCount
   promptType="number"
   i18n-dialogTitle dialogTitle="Non-Cat Checkout"
     <div class="flex-1"></div>
     <div ngbDropdown>
       <button ngbDropdownToggle class="btn mr-2" [ngClass]="{
-          'btn-outline-dark': context.dueDateOptions == 0,
-          'btn-outline-success': context.dueDateOptions > 0
+          'btn-outline-dark': dueDateOptions == 0,
+          'btn-outline-success': dueDateOptions > 0
         }"
         i18n>Date Options</button>
       <div ngbDropdownMenu>
         <button ngbDropdownItem (click)="toggleDateOptions(1)">
-          <span *ngIf="context.dueDateOptions > 0" 
+          <span *ngIf="dueDateOptions > 0" 
             class="badge badge-success">&#x2713;</span>  
-          <span *ngIf="context.dueDateOptions == 0" 
+          <span *ngIf="dueDateOptions == 0" 
             class="badge badge-warning">&#x2717;</span> 
           <span class="pl-2" i18n>Specific Due Date</span>
         </button>
         <button ngbDropdownItem (click)="toggleDateOptions(2)">
-          <span *ngIf="context.dueDateOptions == 2" 
+          <span *ngIf="dueDateOptions == 2" 
             class="badge badge-success">&#x2713;</span>  
-          <span *ngIf="context.dueDateOptions < 2" 
+          <span *ngIf="dueDateOptions < 2" 
             class="badge badge-warning">&#x2717;</span> 
           <span class="pl-2" i18n>Specific Due Date Until Logout</span>
         </button>
       </div>
     </div>
-    <eg-datetime-select (onChangeAsIso)="setDueDate($event)"></eg-datetime-select>
+    <eg-datetime-select [initialIso]="dueDate" 
+      (onChangeAsIso)="setDueDate($event)"></eg-datetime-select>
   </div>
 </div>
 
index 096ff1b..1217c56 100644 (file)
@@ -13,6 +13,10 @@ import {PromptDialogComponent} from '@eg/share/dialog/prompt.component';
 import {GridDataSource, GridColumn, GridCellTextGenerator} from '@eg/share/grid/grid';
 import {GridComponent} from '@eg/share/grid/grid.component';
 import {Pager} from '@eg/share/util/pager';
+import {StoreService} from '@eg/core/store.service';
+import {ServerStoreService} from '@eg/core/server-store.service';
+import {PrecatCheckoutDialogComponent} from './precat-dialog.component';
+import {AudioService} from '@eg/share/util/audio.service';
 
 @Component({
   templateUrl: 'checkout.component.html',
@@ -27,16 +31,21 @@ export class CheckoutComponent implements OnInit {
     cellTextGenerator: GridCellTextGenerator;
     dueDate: string;
     copiesInFlight: {[barcode: string]: boolean} = {};
+    dueDateOptions: 0 | 1 | 2 = 0; // auto date; specific date; session date
 
     @ViewChild('nonCatCount') nonCatCount: PromptDialogComponent;
     @ViewChild('checkoutsGrid') checkoutsGrid: GridComponent;
+    @ViewChild('precatDialog') precatDialog: PrecatCheckoutDialogComponent;
 
     constructor(
+        private store: StoreService,
+        private serverStore: ServerStoreService,
         private org: OrgService,
         private net: NetService,
         public circ: CircService,
         public patronService: PatronService,
-        public context: PatronManagerService
+        public context: PatronManagerService,
+        private audio: AudioService
     ) {}
 
     ngOnInit() {
@@ -49,16 +58,17 @@ export class CheckoutComponent implements OnInit {
         this.cellTextGenerator = {
             title: row => row.title
         };
+
+        if (this.store.getSessionItem('eg.circ.checkout.is_until_logout')) {
+            this.dueDate = this.store.getSessionItem('eg.circ.checkout.due_date');
+            this.toggleDateOptions(2);
+        }
     }
 
     ngAfterViewInit() {
         this.focusInput();
     }
 
-    setDueDate(iso: string) {
-        this.dueDate = iso;
-    }
-
     focusInput() {
         const input = document.getElementById('barcode-input');
         if (input) { input.focus(); }
@@ -91,7 +101,7 @@ export class CheckoutComponent implements OnInit {
             this.copiesInFlight[this.checkoutBarcode] = true;
 
             params.copy_barcode = this.checkoutBarcode;
-            if (this.context.dueDateOptions > 0) { params.due_date = this.dueDate; }
+            if (this.dueDateOptions > 0) { params.due_date = this.dueDate; }
             return Promise.resolve(params);
         }
 
@@ -109,24 +119,35 @@ export class CheckoutComponent implements OnInit {
 
         .then((result: CheckoutResult) => {
             if (result) {
-
                 if (result.params.copy_barcode) {
                     delete this.copiesInFlight[result.params.copy_barcode];
                 }
-
-                if (result.success) {
-                    this.gridifyResult(result);
-                    this.resetForm();
-                }
+                this.dispatchResult(result);
             }
         });
     }
 
+    dispatchResult(result: CheckoutResult) {
+
+        if (result.success) {
+            this.gridifyResult(result);
+            this.resetForm();
+            return;
+        }
+
+        switch (result.evt.textcode) {
+            case 'ITEM_NOT_CATALOGED':
+                this.audio.play('error.checkout.no_cataloged');
+                this.handlePrecat(result);
+                break;
+        }
+    }
+
     resetForm() {
 
-        if (this.context.dueDateOptions < 2) {
+        if (this.dueDateOptions < 2) {
             // Due date is not configured to persist.
-            this.context.dueDateOptions = 0;
+            this.dueDateOptions = 0;
             this.dueDate = null;
         }
 
@@ -137,7 +158,6 @@ export class CheckoutComponent implements OnInit {
 
     gridifyResult(result: CheckoutResult) {
         const entry: CircGridEntry = {
-            title: '',
             copy: result.copy,
             circ: result.circ,
             dueDate: null,
@@ -153,6 +173,13 @@ export class CheckoutComponent implements OnInit {
 
             if (result.record) {
                 entry.title = result.record.title();
+                entry.author = result.record.author();
+                entry.isbn = result.record.isbn();
+
+            } else if (result.copy) {
+                entry.title = result.copy.dummy_title();
+                entry.author = result.copy.dummy_author();
+                entry.isbn = result.copy.dummy_isbn();
             }
 
             if (result.circ) {
@@ -184,19 +211,46 @@ export class CheckoutComponent implements OnInit {
         }));
     }
 
+    setDueDate(iso: string) {
+        this.dueDate = iso;
+        this.store.setSessionItem('eg.circ.checkout.due_date', this.dueDate);
+    }
+
+
     // 0: use server due date
     // 1: use specific due date once
     // 2: use specific due date until the end of the session.
     toggleDateOptions(value: 1 | 2) {
-        if (this.context.dueDateOptions > 0) {
-            if (value === 1) {
-                this.context.dueDateOptions = 0;
-            } else if (this.context.dueDateOptions === 1) {
-                this.context.dueDateOptions = 2;
+        if (this.dueDateOptions > 0) {
+
+            if (value === 1) { // 1 or 2 -> 0
+                this.dueDateOptions = 0;
+                this.store.removeSessionItem('eg.circ.checkout.is_until_logout');
+
+            } else if (this.dueDateOptions === 1) { // 1 -> 2
+
+                this.dueDateOptions = 2;
+                this.store.setSessionItem('eg.circ.checkout.is_until_logout', true);
+
+            } else { // 2 -> 1
+
+                this.dueDateOptions = 1;
+                this.store.removeSessionItem('eg.circ.checkout.is_until_logout');
             }
+
         } else {
-            this.context.dueDateOptions = value;
+
+            this.dueDateOptions = value;
+            if (value === 2) {
+                this.store.setSessionItem('eg.circ.checkout.is_until_logout', true);
+            }
         }
     }
+
+    handlePrecat(result: CheckoutResult) {
+        this.precatDialog.open({size: 'lg'}).subscribe(values => {
+            console.log('precat values', values);
+        })
+    }
 }
 
index 36e0429..ced4ffc 100644 (file)
@@ -15,6 +15,7 @@ import {HoldsComponent} from './holds.component';
 import {EditComponent} from './edit.component';
 import {EditToolbarComponent} from './edit-toolbar.component';
 import {BcSearchComponent} from './bcsearch.component';
+import {PrecatCheckoutDialogComponent} from './precat-dialog.component';
 
 @NgModule({
   declarations: [
@@ -24,7 +25,8 @@ import {BcSearchComponent} from './bcsearch.component';
     HoldsComponent,
     EditComponent,
     EditToolbarComponent,
-    BcSearchComponent
+    BcSearchComponent,
+    PrecatCheckoutDialogComponent
   ],
   imports: [
     StaffCommonModule,
index 7bb36d7..dd66a15 100644 (file)
@@ -7,6 +7,8 @@ import {PatronSearch} from '@eg/staff/share/patron/search.component';
 
 export interface CircGridEntry {
     title?: string;
+    author?: string;
+    isbn?: string;
     copy?: IdlObject;
     circ?: IdlObject;
     dueDate?: string;
@@ -70,7 +72,6 @@ export class PatronManagerService {
 
     // These should persist tab changes
     checkouts: CircGridEntry[] = [];
-    dueDateOptions: 0 | 1 | 2 = 0; // auto date; specific date; session date
 
     constructor(
         private net: NetService,
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/precat-dialog.component.html b/Open-ILS/src/eg2/src/app/staff/circ/patron/precat-dialog.component.html
new file mode 100644 (file)
index 0000000..2b3d45e
--- /dev/null
@@ -0,0 +1,20 @@
+<ng-template #dialogContent>
+  <div class="modal-header bg-info">
+    <h4 i18n>Barcode "{{barcode}}" was mis-scanned or is a non-cataloged item.</h4>
+    <button type="button" class="close"
+      i18n-aria-label aria-label="Close" (click)="close()">
+      <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+  <div class="modal-body">
+    <div class="row">
+      <div class="col-lg-4" i18n>Circulation Modifier</div>
+      <div class="col-lg-8">
+        <eg-combobox idlClass="ccm" [(ngModel)]="circModifier"></eg-combobox>
+      </div>
+    </div>
+  </div>
+  <div class="modal-footer">
+  <!-- has perm -->
+  </div>
+</ng-template>
diff --git a/Open-ILS/src/eg2/src/app/staff/circ/patron/precat-dialog.component.ts b/Open-ILS/src/eg2/src/app/staff/circ/patron/precat-dialog.component.ts
new file mode 100644 (file)
index 0000000..d440876
--- /dev/null
@@ -0,0 +1,37 @@
+import {Component, OnInit, Input} from '@angular/core';
+import {Observable} from 'rxjs';
+import {DialogComponent} from '@eg/share/dialog/dialog.component';
+import {NgbModal, NgbModalOptions} from '@ng-bootstrap/ng-bootstrap';
+import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
+import {PermService} from '@eg/core/perm.service';
+
+/**
+ * Precat checkout dialog
+ */
+
+@Component({
+  selector: 'eg-precat-checkout-dialog',
+  templateUrl: 'precat-dialog.component.html'
+})
+
+export class PrecatCheckoutDialogComponent extends DialogComponent implements OnInit {
+
+    @Input() barcode = '';
+
+    circModifier: ComboboxEntry;
+    hasPerm = false;
+
+    constructor(
+        private perm: PermService,
+        private modal: NgbModal) {
+        super(modal);
+    }
+
+    ngOnInit() {
+        this.perm.hasWorkPermHere('CREATE_PRECAT')
+        .then(perms => this.hasPerm = perms['CREATE_PRECAT']);
+    }
+}
+
+
+
index 10a9bc2..dee1b99 100644 (file)
@@ -25,6 +25,7 @@ export interface CheckoutParams {
 
 export interface CheckoutResult {
     index: number;
+    evt: EgEvent;
     params: CheckoutParams,
     success: boolean;
     copy?: IdlObject;
@@ -62,13 +63,15 @@ export class CircService {
         ).toPromise().then(types => this.nonCatTypes = types);
     }
 
-    checkout(params: CheckoutParams): Promise<CheckoutResult> {
+    checkout(params: CheckoutParams, override?: boolean): Promise<CheckoutResult> {
 
-        console.log('checking out with', params);
+        console.debug('checking out with', params);
+
+        let method = 'open-ils.circ.checkout.full';
+        if (override) { method += '.override'; }
 
         return this.net.request(
-            'open-ils.circ',
-            'open-ils.circ.checkout.full',
+            'open-ils.circ', method,
             this.auth.token(), params).toPromise()
         .then(result => this.processCheckoutResult(params, result));
     }
@@ -90,8 +93,9 @@ export class CircService {
 
         const result: CheckoutResult = {
             index: CircService.resultIndex++,
+            evt: evt,
             params: params,
-            success: true,
+            success: evt.textcode === 'SUCCESS',
             circ: payload.circ,
             copy: payload.copy,
             record: payload.record,