From 26bd88465fee2843fa8627bb6b049f61320b1814 Mon Sep 17 00:00:00 2001 From: Bill Erickson Date: Thu, 8 Jul 2021 10:19:43 -0400 Subject: [PATCH] LP1933275 Staff catalog holdings view shows correct counts teaches the Holdings view to determine the number of copies and call numbers attached to each org unit based on the full data set (via new API) instead of copies in hand, since we may only have copies in hand for a subset of child org units. Signed-off-by: Bill Erickson --- .../app/staff/catalog/record/holdings.component.ts | 58 +++++++++-------- .../lib/OpenILS/Application/Search/Biblio.pm | 72 ++++++++++++++++++++++ 2 files changed, 106 insertions(+), 24 deletions(-) diff --git a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts index 2a58ef605c..4d966bf0c1 100644 --- a/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts +++ b/Open-ILS/src/eg2/src/app/staff/catalog/record/holdings.component.ts @@ -2,13 +2,14 @@ import {Component, OnInit, Input, ViewChild, ViewEncapsulation } from '@angular/core'; import {Router} from '@angular/router'; import {Observable, Observer, of, empty} from 'rxjs'; -import {map} from 'rxjs/operators'; +import {map, tap, concatMap} from 'rxjs/operators'; import {Pager} from '@eg/share/util/pager'; import {IdlObject, IdlService} from '@eg/core/idl.service'; import {StaffCatalogService} from '../catalog.service'; import {OrgService} from '@eg/core/org.service'; import {PcrudService} from '@eg/core/pcrud.service'; import {AuthService} from '@eg/core/auth.service'; +import {NetService} from '@eg/core/net.service'; import {GridDataSource, GridColumn, GridCellTextGenerator} from '@eg/share/grid/grid'; import {GridComponent} from '@eg/share/grid/grid.component'; import {GridToolbarCheckboxComponent @@ -146,6 +147,8 @@ export class HoldingsMaintenanceComponent implements OnInit { rowClassCallback: (row: any) => string; cellTextGenerator: GridCellTextGenerator; + copyCounts: {[orgId: number]: {}} = {}; + private _recId: number; @Input() set recordId(id: number) { this._recId = id; @@ -172,6 +175,7 @@ export class HoldingsMaintenanceComponent implements OnInit { private idl: IdlService, private pcrud: PcrudService, private auth: AuthService, + private net: NetService, private staffCat: StaffCatalogService, private store: ServerStoreService, private localStore: StoreService, @@ -384,8 +388,8 @@ export class HoldingsMaintenanceComponent implements OnInit { setTreeCounts(node: HoldingsTreeNode) { if (node.nodeType === 'org') { - node.copyCount = 0; - node.callNumCount = 0; + node.copyCount = this.copyCounts[node.target.id() + ''].copies; + node.callNumCount = this.copyCounts[node.target.id() + ''].call_numbers; } else if (node.nodeType === 'callNum') { node.copyCount = 0; } @@ -395,13 +399,9 @@ export class HoldingsMaintenanceComponent implements OnInit { node.children.forEach(child => { this.setTreeCounts(child); if (node.nodeType === 'org') { - node.copyCount += child.copyCount; - if (child.nodeType === 'callNum') { - node.callNumCount++; - } else { + if (child.nodeType !== 'callNum') { hasChildOrgWithData = child.callNumCount > 0; hasChildOrgSansData = child.callNumCount === 0; - node.callNumCount += child.callNumCount; } } else if (node.nodeType === 'callNum') { node.copyCount = node.children.length; @@ -505,21 +505,31 @@ export class HoldingsMaintenanceComponent implements OnInit { this.itemCircsNeeded = []; - this.pcrud.search('acn', - { record: this.recordId, - owning_lib: this.org.fullPath(this.contextOrg, true), - deleted: 'f', - label: {'!=' : '##URI##'} - }, { - flesh: 3, - flesh_fields: { - acp: ['status', 'location', 'circ_lib', 'parts', 'notes', - 'tags', 'age_protect', 'copy_alerts', 'latest_inventory'], - acn: ['prefix', 'suffix', 'copies'], - acli: ['inventory_workstation'] - } - }, - {authoritative: true} + return this.net.request( + 'open-ils.search', + 'open-ils.search.biblio.record.copy_counts.global.staff', + this.recordId + ).pipe( + tap(counts => this.copyCounts = counts), + concatMap(_ => { + + return this.pcrud.search('acn', + { record: this.recordId, + owning_lib: this.org.fullPath(this.contextOrg, true), + deleted: 'f', + label: {'!=' : '##URI##'} + }, { + flesh: 3, + flesh_fields: { + acp: ['status', 'location', 'circ_lib', 'parts', 'notes', + 'tags', 'age_protect', 'copy_alerts', 'latest_inventory'], + acn: ['prefix', 'suffix', 'copies'], + acli: ['inventory_workstation'] + } + }, + {authoritative: true} + ); + }) ).subscribe( callNum => this.appendCallNum(callNum), err => {}, @@ -529,7 +539,7 @@ export class HoldingsMaintenanceComponent implements OnInit { ok => this.flattenHoldingsTree(observer) ); } - ); + ); }); } diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm index 95720a8eeb..c91cdb81c7 100644 --- a/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm +++ b/Open-ILS/src/perlmods/lib/OpenILS/Application/Search/Biblio.pm @@ -3175,6 +3175,78 @@ sub get_one_record_summary { }; } +__PACKAGE__->register_method( + method => 'record_copy_counts_global', + api_name => 'open-ils.search.biblio.record.copy_counts.global.staff', + signature => { + desc => q/Returns a count of copies and call numbers for each org + unit, including items attached to each org unit plus + a sum of counts for all descendants./, + params => [ + {desc => 'Record ID', type => 'number'} + ], + return => { + desc => 'Hash of org unit ID => {copy: $count, call_number: $id}' + } + } +); + +sub record_copy_counts_global { + my ($self, $client, $rec_id) = @_; + + my $copies = new_editor()->json_query({ + select => { + acp => [{column => 'id', alias => 'copy_id'}, 'circ_lib'], + acn => [{column => 'id', alias => 'cn_id'}, 'owning_lib'] + }, + from => {acn => {acp => {type => 'left'}}}, + where => { + '+acp' => { + '-or' => [ + {deleted => 'f'}, + {id => undef} # left join + ] + }, + '+acn' => {deleted => 'f', record => $rec_id} + } + }); + + my $hash = {}; + my %seen_cn; + + for my $copy (@$copies) { + my $org = $copy->{circ_lib} || $copy->{owning_lib}; + $hash->{$org} = {copies => 0, call_numbers => 0} unless $hash->{$org}; + $hash->{$org}->{copies}++ if $copy->{circ_lib}; + + if (!$seen_cn{$copy->{cn_id}}) { + $seen_cn{$copy->{cn_id}} = 1; + $hash->{$org}->{call_numbers}++; + } + } + + my $sum; + $sum = sub { + my $node = shift; + my $h = $hash->{$node->id} || {copies => 0, call_numbers => 0}; + delete $h->{cn_id}; + + for my $child (@{$node->children}) { + my $vals = $sum->($child); + $h->{copies} += $vals->{copies}; + $h->{call_numbers} += $vals->{call_numbers}; + } + + $hash->{$node->id} = $h; + + return $h; + }; + + $sum->($U->get_org_tree); + + return $hash; +} + 1; -- 2.11.0