LP1929741 ACQ caching / batch editing improvements
authorBill Erickson <berickxx@gmail.com>
Wed, 20 Jan 2021 20:49:55 +0000 (15:49 -0500)
committerJane Sandberg <js7389@princeton.edu>
Sun, 2 Oct 2022 15:02:49 +0000 (08:02 -0700)
Ensure that funds/mods/locations of modified copies are available at
render time after saving changes to copies.

Hide the copy batch update bar when no copies are editable.

Signed-off-by: Bill Erickson <berickxx@gmail.com>
Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Signed-off-by: Jane Sandberg <js7389@princeton.edu>
Open-ILS/src/eg2/src/app/staff/acq/lineitem/batch-copies.component.html
Open-ILS/src/eg2/src/app/staff/acq/lineitem/batch-copies.component.ts
Open-ILS/src/eg2/src/app/staff/acq/lineitem/copies.component.ts
Open-ILS/src/eg2/src/app/staff/acq/lineitem/copy-attrs.component.ts
Open-ILS/src/eg2/src/app/staff/acq/lineitem/lineitem.service.ts

index 615ddb0..de3b2f4 100644 (file)
   </div>
 </ng-template>
 
-<ng-container
-  *ngTemplateOutlet="copyAttrsHeader;context:{
-    moreCss:'mt-3 bg-light border border-secondary',
-    hideBarcode: true
-  }">
+<ng-container *ngIf="hasEditableCopies()">
+  <ng-container
+    *ngTemplateOutlet="copyAttrsHeader;context:{
+      moreCss:'mt-3 bg-light border border-secondary',
+      hideBarcode: true
+    }">
+  </ng-container>
+  
+  <div class="pt-2 bg-light border border-secondary border-top-0 rounded-bottom">
+    <eg-lineitem-copy-attrs (batchApplyRequested)="batchApplyAttrs($event)"
+      [batchMode]="true"> </eg-lineitem-copy-attrs>
+  </div>
 </ng-container>
 
-<div class="pt-2 bg-light border border-secondary border-top-0 rounded-bottom">
-  <eg-lineitem-copy-attrs (batchApplyRequested)="batchApplyAttrs($event)"
-    [batchMode]="true"> </eg-lineitem-copy-attrs>
-</div>
-
 <hr/>
 
 <ng-container *ngTemplateOutlet="copyAttrsHeader"> </ng-container>
index 592d723..8ce878c 100644 (file)
@@ -133,6 +133,21 @@ export class LineitemBatchCopiesComponent implements OnInit {
 
         return promise;
     }
+
+    hasEditableCopies(): boolean {
+        if (this.lineitem) {
+            const copies = this.lineitem.lineitem_details();
+            if (copies && copies.length > 0) {
+                for (let idx = 0; idx < copies.length; idx++) { // early break
+                    if (this.liService.copyDisposition(
+                        this.lineitem, copies[idx]) === 'pre-order') {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
 }
 
 
index 3ad72df..e9f060e 100644 (file)
@@ -171,51 +171,22 @@ export class LineitemCopiesComponent implements OnInit, AfterViewInit {
     // are not required to go fetch them en masse / en duplicato.
     fetchFormulaValues(): Promise<any> {
 
-        const funds = this.formulaValues.fund ?
-            Object.keys(this.formulaValues.fund) : [];
-
-        const mods = this.formulaValues.circ_modifier ?
-            Object.keys(this.formulaValues.circ_modifier) : [];
-
-        const locs = this.formulaValues.location ?
-            Object.keys(this.formulaValues.location) : [];
-
-        let promise = Promise.resolve();
-
-        if (funds.length > 0) {
-            promise = promise.then(_ => {
-                return this.pcrud.search('acqf', {id: funds})
-                .pipe(tap(fund => {
-                    this.liService.fundCache[fund.id()] = fund;
-                    this.liService.batchOptionWanted.emit(
-                        {fund: {id: fund.id(), label: fund.code(), fm: fund}});
-                })).toPromise();
-            });
+        let funds = [];
+        if (this.formulaValues.fund) {
+            funds = Object.keys(this.formulaValues.fund).map(id => Number(id));
         }
 
-        if (mods.length > 0) {
-            promise = promise.then(_ => {
-                return this.pcrud.search('ccm', {code: mods})
-                .pipe(tap(mod => {
-                    this.liService.circModCache[mod.code()] = mod;
-                    this.liService.batchOptionWanted.emit({circ_modifier:
-                        {id: mod.code(), label: mod.code(), fm: mod}});
-                })).toPromise();
-            });
+        let locs = [];
+        if (this.formulaValues.location) {
+            locs = Object.keys(this.formulaValues.location).map(id => Number(id));
         }
 
-        if (locs.length > 0) {
-            promise = promise.then(_ => {
-                return this.pcrud.search('acpl', {id: locs})
-                .pipe(tap(loc => {
-                    this.loc.locationCache[loc.id()] = loc;
-                    this.liService.batchOptionWanted.emit({location:
-                        {id: loc.id(), label: loc.name(), fm: loc}});
-                })).toPromise();
-            });
-        }
+        const mods = this.formulaValues.circ_modifier ?
+            Object.keys(this.formulaValues.circ_modifier) : [];
 
-        return promise;
+        return this.liService.fetchFunds(funds)
+            .then(_ => this.liService.fetchLocations(locs))
+            .then(_ => this.liService.fetchCircMods(mods));
     }
 
     // Apply a formula entry to a single copy.
index 94b9e6d..fb9ecae 100644 (file)
@@ -4,7 +4,7 @@ import {Pager} from '@eg/share/util/pager';
 import {IdlObject, IdlService} from '@eg/core/idl.service';
 import {NetService} from '@eg/core/net.service';
 import {AuthService} from '@eg/core/auth.service';
-import {LineitemService} from './lineitem.service';
+import {LineitemService, COPY_ORDER_DISPOSITION} from './lineitem.service';
 import {ComboboxComponent, ComboboxEntry} from '@eg/share/combobox/combobox.component';
 import {ItemLocationService} from '@eg/share/item-location-select/item-location-select.service';
 import {ItemLocationSelectComponent} from '@eg/share/item-location-select/item-location-select.component';
@@ -152,20 +152,8 @@ export class LineitemCopyAttrsComponent implements OnInit {
         return false;
     }
 
-    disposition(): 'canceled' | 'delayed' | 'received' | 'on-order' | 'pre-order' {
-        if (!this.copy || !this.lineitem) {
-            return null;
-        } else if (this.copy.cancel_reason()) {
-            if (this.copy.cancel_reason().keep_debits() === 't') {
-                return 'delayed';
-            } else {
-                return 'canceled';
-            }
-        } else if (this.copy.recv_time()) {
-            return 'received';
-        } else if (this.lineitem.state() === 'on-order') {
-            return 'on-order';
-        } else { return 'pre-order'; }
+    disposition(): COPY_ORDER_DISPOSITION {
+        return this.liService.copyDisposition(this.lineitem, this.copy);
     }
 }
 
index a4e49a1..9ee2f73 100644 (file)
@@ -8,6 +8,10 @@ import {PcrudService} from '@eg/core/pcrud.service';
 import {ComboboxEntry} from '@eg/share/combobox/combobox.component';
 import {ItemLocationService} from '@eg/share/item-location-select/item-location-select.service';
 
+const COPY_ORDER_DISPOSITIONS:
+    'canceled' | 'delayed' | 'received' | 'on-order' | 'pre-order' = null;
+export type COPY_ORDER_DISPOSITION = typeof COPY_ORDER_DISPOSITIONS;
+
 export interface BatchLineitemStruct {
     id: number;
     lineitem: IdlObject;
@@ -274,9 +278,21 @@ export class LineitemService {
         const lids = li.lineitem_details().filter(copy =>
             (copy.isnew() || copy.ischanged() || copy.isdeleted()));
 
-        return this.net.request(
-            'open-ils.acq',
-            'open-ils.acq.lineitem_detail.cud.batch', this.auth.token(), lids);
+        return from(
+
+            // Ensure we have the updated fund/loc/mod values before
+            // sending the copies off to be updated and then re-drawn.
+            this.fetchFunds(lids.map(lid => lid.fund()))
+            .then(_ => this.fetchLocations(lids.map(lid => lid.location())))
+            .then(_ => this.fetchCircMods(lids.map(lid => lid.circ_modifier())))
+
+        ).pipe(switchMap(_ =>
+            this.net.request(
+                'open-ils.acq',
+                'open-ils.acq.lineitem_detail.cud.batch',
+                this.auth.token(), lids
+            )
+        ));
     }
 
     updateLineitems(lis: IdlObject[]): Observable<BatchLineitemUpdateStruct> {
@@ -296,5 +312,63 @@ export class LineitemService {
 
         return obs;
     }
+
+
+    // Methods to fetch copy-related data, add it to our local cache,
+    // and announce that new values are available for comboboxes.
+    fetchFunds(fundIds: number[]): Promise<any> {
+        fundIds = fundIds.filter(id => id && !(id in this.fundCache));
+        if (fundIds.length === 0) { return Promise.resolve(); }
+
+        return this.pcrud.search('acqf', {id: fundIds})
+        .pipe(tap(fund => {
+            this.fundCache[fund.id()] = fund;
+            this.batchOptionWanted.emit(
+                {fund: {id: fund.id(), label: fund.code(), fm: fund}});
+        })).toPromise();
+    }
+
+    fetchCircMods(circMods: string[]): Promise<any> {
+        circMods = circMods
+            .filter(code => code && !(code in this.circModCache));
+
+        if (circMods.length === 0) { return Promise.resolve(); }
+
+        return this.pcrud.search('ccm', {code: circMods})
+        .pipe(tap(mod => {
+            this.circModCache[mod.code()] = mod;
+            this.batchOptionWanted.emit({circ_modifier:
+                {id: mod.code(), label: mod.code(), fm: mod}});
+        })).toPromise();
+    }
+
+    fetchLocations(locIds: number[]): Promise<any> {
+        locIds = locIds.filter(id => id && !(id in this.loc.locationCache));
+        if (locIds.length === 0) { return Promise.resolve(); }
+
+        return this.pcrud.search('acpl', {id: locIds})
+        .pipe(tap(loc => {
+            this.loc.locationCache[loc.id()] = loc;
+            this.batchOptionWanted.emit({location:
+                {id: loc.id(), label: loc.name(), fm: loc}});
+        })).toPromise();
+    }
+
+    // Order disposition of a single lineitem detail
+    copyDisposition(lineitem: IdlObject, copy: IdlObject): COPY_ORDER_DISPOSITION {
+        if (!copy || !lineitem) {
+            return null;
+        } else if (copy.cancel_reason()) {
+            if (copy.cancel_reason().keep_debits() === 't') {
+                return 'delayed';
+            } else {
+                return 'canceled';
+            }
+        } else if (copy.recv_time()) {
+            return 'received';
+        } else if (lineitem.state() === 'on-order') {
+            return 'on-order';
+        } else { return 'pre-order'; }
+    }
 }