1 package OpenILS::Application::AppUtils;
3 use strict; use warnings;
4 use OpenILS::Application;
5 use base qw/OpenILS::Application/;
6 use OpenSRF::Utils::Cache;
7 use OpenSRF::Utils::Logger qw/$logger/;
8 use OpenILS::Utils::ModsParser;
9 use OpenSRF::EX qw(:try);
12 use OpenILS::Utils::CStoreEditor;
13 use OpenILS::Const qw/:const/;
15 # ---------------------------------------------------------------------------
16 # Pile of utilty methods used accross applications.
17 # ---------------------------------------------------------------------------
18 my $cache_client = "OpenSRF::Utils::Cache";
21 # ---------------------------------------------------------------------------
22 # on sucess, returns the created session, on failure throws ERROR exception
23 # ---------------------------------------------------------------------------
24 sub start_db_session {
27 my $session = OpenSRF::AppSession->connect( "open-ils.storage" );
28 my $trans_req = $session->request( "open-ils.storage.transaction.begin" );
30 my $trans_resp = $trans_req->recv();
31 if(ref($trans_resp) and UNIVERSAL::isa($trans_resp,"Error")) { throw $trans_resp; }
32 if( ! $trans_resp->content() ) {
34 ("Unable to Begin Transaction with database" );
38 $logger->debug("Setting global storage session to ".
39 "session: " . $session->session_id . " : " . $session->app );
45 # returns undef if user has all of the perms provided
46 # returns the first failed perm on failure
47 sub check_user_perms {
48 my($self, $user_id, $org_id, @perm_types ) = @_;
49 $logger->debug("Checking perms with user : $user_id , org: $org_id, @perm_types");
50 for my $type (@perm_types) {
51 return $type unless ($self->storagereq(
52 "open-ils.storage.permission.user_has_perm",
53 $user_id, $type, $org_id ));
58 # checks the list of user perms. The first one that fails returns a new
60 my( $self, $user_id, $org_id, @perm_types ) = @_;
61 my $t = $self->check_user_perms( $user_id, $org_id, @perm_types );
62 return OpenILS::Event->new('PERM_FAILURE', ilsperm => $t, ilspermloc => $org_id ) if $t;
68 # ---------------------------------------------------------------------------
69 # commits and destroys the session
70 # ---------------------------------------------------------------------------
71 sub commit_db_session {
72 my( $self, $session ) = @_;
74 my $req = $session->request( "open-ils.storage.transaction.commit" );
75 my $resp = $req->recv();
78 throw OpenSRF::EX::ERROR ("Unable to commit db session");
81 if(UNIVERSAL::isa($resp,"Error")) {
82 throw $resp ($resp->stringify);
86 throw OpenSRF::EX::ERROR ("Unable to commit db session");
90 $session->disconnect();
94 sub rollback_db_session {
95 my( $self, $session ) = @_;
97 my $req = $session->request("open-ils.storage.transaction.rollback");
98 my $resp = $req->recv();
99 if(UNIVERSAL::isa($resp,"Error")) { throw $resp; }
102 $session->disconnect();
107 # returns undef it the event is not an ILS event
108 # returns the event code otherwise
110 my( $self, $evt ) = @_;
111 return $evt->{ilsevent} if( ref($evt) eq 'HASH' and defined($evt->{ilsevent})) ;
115 # ---------------------------------------------------------------------------
116 # Checks to see if a user is logged in. Returns the user record on success,
117 # throws an exception on error.
118 # ---------------------------------------------------------------------------
119 sub check_user_session {
121 my( $self, $user_session ) = @_;
123 my $content = $self->simplereq(
125 'open-ils.auth.session.retrieve', $user_session );
127 if(! $content or $self->event_code($content)) {
128 throw OpenSRF::EX::ERROR
129 ("Session [$user_session] cannot be authenticated" );
132 $logger->debug("Fetch user session $user_session found user " . $content->id );
137 # generic simple request returning a scalar value
139 my($self, $service, $method, @params) = @_;
140 return $self->simple_scalar_request($service, $method, @params);
144 sub simple_scalar_request {
145 my($self, $service, $method, @params) = @_;
147 my $session = OpenSRF::AppSession->create( $service );
149 my $request = $session->request( $method, @params );
155 $val = $request->gather(1);
162 warn "received error : service=$service : method=$method : params=".Dumper(\@params) . "\n $err";
163 throw $err ("Call to $service for method $method \n failed with exception: $err : " );
175 my $org_typelist = undef;
176 my $org_typelist_hash = {};
181 if($tree) { return $tree; }
183 # see if it's in the cache
184 $tree = $cache_client->new()->get_cache('_orgtree');
185 if($tree) { return $tree; }
188 warn "Retrieving Org Tree\n";
189 $orglist = $self->simple_scalar_request(
191 "open-ils.cstore.direct.actor.org_unit.search.atomic",
192 { id => { '!=' => undef } }
196 if( ! $org_typelist ) {
197 warn "Retrieving org types\n";
198 $org_typelist = $self->simple_scalar_request(
200 "open-ils.cstore.direct.actor.org_unit_type.search.atomic",
201 { id => { '!=' => undef } }
203 $self->build_org_type($org_typelist);
206 $tree = $self->build_org_tree($orglist,1);
207 $cache_client->new()->put_cache('_orgtree', $tree);
212 my $slimtree = undef;
213 sub get_slim_org_tree {
216 if($slimtree) { return $slimtree; }
218 # see if it's in the cache
219 $slimtree = $cache_client->new()->get_cache('slimorgtree');
220 if($slimtree) { return $slimtree; }
223 warn "Retrieving Org Tree\n";
224 $orglist = $self->simple_scalar_request(
226 "open-ils.cstore.direct.actor.org_unit.search.atomic",
227 { id => { '!=' => undef } }
231 $slimtree = $self->build_org_tree($orglist);
232 $cache_client->new->put_cache('slimorgtree', $slimtree);
239 my($self, $org_typelist) = @_;
240 for my $type (@$org_typelist) {
241 $org_typelist_hash->{$type->id()} = $type;
249 my( $self, $orglist, $add_types ) = @_;
251 return $orglist unless ref $orglist;
252 return $$orglist[0] if @$orglist == 1;
255 $a->ou_type <=> $b->ou_type ||
256 $a->name cmp $b->name } @$orglist;
258 for my $org (@list) {
262 if(!ref($org->ou_type()) and $add_types) {
263 $org->ou_type( $org_typelist_hash->{$org->ou_type()});
266 next unless (defined($org->parent_ou));
268 my ($parent) = grep { $_->id == $org->parent_ou } @list;
270 $parent->children([]) unless defined($parent->children);
271 push( @{$parent->children}, $org );
277 sub fetch_closed_date {
278 my( $self, $cd ) = @_;
281 $logger->debug("Fetching closed_date $cd from cstore");
283 my $cd_obj = $self->simplereq(
285 'open-ils.cstore.direct.actor.org_unit.closed_date.retrieve', $cd );
288 $logger->info("closed_date $cd not found in the db");
289 $evt = OpenILS::Event->new('ACTOR_USER_NOT_FOUND');
292 return ($cd_obj, $evt);
296 my( $self, $userid ) = @_;
299 $logger->debug("Fetching user $userid from cstore");
301 $user = $self->simplereq(
303 'open-ils.cstore.direct.actor.user.retrieve', $userid );
306 $logger->info("User $userid not found in the db");
307 $evt = OpenILS::Event->new('ACTOR_USER_NOT_FOUND');
310 return ($user, $evt);
314 my( $self, $session ) = @_;
315 my $user; my $evt; my $e;
317 $logger->debug("Checking user session $session");
320 $user = $self->check_user_session($session);
321 } catch Error with { $e = 1; };
323 $logger->debug("Done checking user session $session " . (($e) ? "error = $e" : "") );
325 if( $e or !$user ) { $evt = OpenILS::Event->new('NO_SESSION'); }
326 return ( $user, $evt );
330 # verifiese the session and checks the permissions agains the
331 # session user and the user's home_ou as the org id
333 my( $self, $session, @perms ) = @_;
334 my $user; my $evt; my $e;
335 $logger->debug("Checking user session $session and perms @perms");
336 ($user, $evt) = $self->checkses($session);
337 return (undef, $evt) if $evt;
338 $evt = $self->check_perms($user->id, $user->home_ou, @perms);
339 return ($user, $evt);
344 my( $self, $staffobj, $userid, @perms ) = @_;
346 $userid = $staffobj->id unless defined $userid;
348 $logger->debug("checkrequestor(): requestor => " . $staffobj->id . ", target => $userid");
350 if( $userid ne $staffobj->id ) {
351 ($user, $evt) = $self->fetch_user($userid);
352 return (undef, $evt) if $evt;
353 $evt = $self->check_perms( $staffobj->id, $user->home_ou, @perms );
359 return ($user, $evt);
362 sub checkses_requestor {
363 my( $self, $authtoken, $targetid, @perms ) = @_;
364 my( $requestor, $target, $evt );
366 ($requestor, $evt) = $self->checkses($authtoken);
367 return (undef, undef, $evt) if $evt;
369 ($target, $evt) = $self->checkrequestor( $requestor, $targetid, @perms );
370 return( $requestor, $target, $evt);
374 my( $self, $copyid ) = @_;
377 $logger->debug("Fetching copy $copyid from cstore");
379 $copy = $self->simplereq(
381 'open-ils.cstore.direct.asset.copy.retrieve', $copyid );
383 if(!$copy) { $evt = OpenILS::Event->new('ASSET_COPY_NOT_FOUND'); }
385 return( $copy, $evt );
389 # retrieves a circ object by id
390 sub fetch_circulation {
391 my( $self, $circid ) = @_;
394 $logger->debug("Fetching circ $circid from cstore");
396 $circ = $self->simplereq(
398 "open-ils.cstore.direct.action.circulation.retrieve", $circid );
401 $evt = OpenILS::Event->new('ACTION_CIRCULATION_NOT_FOUND', circid => $circid );
404 return ( $circ, $evt );
407 sub fetch_record_by_copy {
408 my( $self, $copyid ) = @_;
411 $logger->debug("Fetching record by copy $copyid from cstore");
413 $record = $self->simplereq(
415 'open-ils.cstore.direct.asset.copy.retrieve', $copyid,
417 flesh_fields => { bre => [ 'fixed_fields' ],
419 acp => [ 'call_number' ],
425 $evt = OpenILS::Event->new('BIBLIO_RECORD_ENTRY_NOT_FOUND');
427 $record = $record->call_number->record;
430 return ($record, $evt);
433 # turns a record object into an mvr (mods) object
435 my( $self, $record ) = @_;
436 return undef unless $record and $record->marc;
437 my $u = OpenILS::Utils::ModsParser->new();
438 $u->start_mods_batch( $record->marc );
439 my $mods = $u->finish_mods_batch();
440 $mods->doc_id($record->id);
441 $mods->tcn($record->tcn_value);
446 my( $self, $holdid ) = @_;
449 $logger->debug("Fetching hold $holdid from cstore");
451 $hold = $self->simplereq(
453 'open-ils.cstore.direct.action.hold_request.retrieve', $holdid);
455 $evt = OpenILS::Event->new('ACTION_HOLD_REQUEST_NOT_FOUND', holdid => $holdid) unless $hold;
457 return ($hold, $evt);
461 sub fetch_hold_transit_by_hold {
462 my( $self, $holdid ) = @_;
463 my( $transit, $evt );
465 $logger->debug("Fetching transit by hold $holdid from cstore");
467 $transit = $self->simplereq(
469 'open-ils.cstore.direct.action.hold_transit_copy.search', { hold => $holdid } );
471 $evt = OpenILS::Event->new('ACTION_HOLD_TRANSIT_COPY_NOT_FOUND', holdid => $holdid) unless $transit;
473 return ($transit, $evt );
476 # fetches the captured, but not fulfilled hold attached to a given copy
477 sub fetch_open_hold_by_copy {
478 my( $self, $copyid ) = @_;
479 $logger->debug("Searching for active hold for copy $copyid");
482 $hold = $self->cstorereq(
483 'open-ils.cstore.direct.action.hold_request.search',
485 current_copy => $copyid ,
486 capture_time => { "!=" => undef },
487 fulfillment_time => undef,
488 cancel_time => undef,
491 $evt = OpenILS::Event->new('ACTION_HOLD_REQUEST_NOT_FOUND', copyid => $copyid) unless $hold;
492 return ($hold, $evt);
495 sub fetch_hold_transit {
496 my( $self, $transid ) = @_;
497 my( $htransit, $evt );
498 $logger->debug("Fetching hold transit with hold id $transid");
499 $htransit = $self->cstorereq(
500 'open-ils.cstore.direct.action.hold_transit_copy.retrieve', $transid );
501 $evt = OpenILS::Event->new('ACTION_HOLD_TRANSIT_COPY_NOT_FOUND', id => $transid) unless $htransit;
502 return ($htransit, $evt);
505 sub fetch_copy_by_barcode {
506 my( $self, $barcode ) = @_;
509 $logger->debug("Fetching copy by barcode $barcode from cstore");
511 $copy = $self->simplereq( 'open-ils.cstore',
512 'open-ils.cstore.direct.asset.copy.search', { barcode => $barcode, deleted => 'f'} );
513 #'open-ils.storage.direct.asset.copy.search.barcode', $barcode );
515 $evt = OpenILS::Event->new('ASSET_COPY_NOT_FOUND', barcode => $barcode) unless $copy;
517 return ($copy, $evt);
520 sub fetch_open_billable_transaction {
521 my( $self, $transid ) = @_;
522 my( $transaction, $evt );
524 $logger->debug("Fetching open billable transaction $transid from cstore");
526 $transaction = $self->simplereq(
528 'open-ils.cstore.direct.money.open_billable_transaction_summary.retrieve', $transid);
530 $evt = OpenILS::Event->new(
531 'MONEY_OPEN_BILLABLE_TRANSACTION_SUMMARY_NOT_FOUND', transid => $transid ) unless $transaction;
533 return ($transaction, $evt);
539 $buckets{'biblio'} = 'biblio_record_entry_bucket';
540 $buckets{'callnumber'} = 'call_number_bucket';
541 $buckets{'copy'} = 'copy_bucket';
542 $buckets{'user'} = 'user_bucket';
544 sub fetch_container {
545 my( $self, $id, $type ) = @_;
548 $logger->debug("Fetching container $id with type $type");
550 my $e = 'CONTAINER_CALL_NUMBER_BUCKET_NOT_FOUND';
551 $e = 'CONTAINER_BIBLIO_RECORD_ENTRY_BUCKET_NOT_FOUND' if $type eq 'biblio';
552 $e = 'CONTAINER_USER_BUCKET_NOT_FOUND' if $type eq 'user';
553 $e = 'CONTAINER_COPY_BUCKET_NOT_FOUND' if $type eq 'copy';
555 my $meth = $buckets{$type};
556 $bucket = $self->simplereq(
558 "open-ils.cstore.direct.container.$meth.retrieve", $id );
560 $evt = OpenILS::Event->new(
561 $e, container => $id, container_type => $type ) unless $bucket;
563 return ($bucket, $evt);
567 sub fetch_container_e {
568 my( $self, $editor, $id, $type ) = @_;
571 $bucket = $editor->retrieve_container_copy_bucket($id) if $type eq 'copy';
572 $bucket = $editor->retrieve_container_call_number_bucket($id) if $type eq 'callnumber';
573 $bucket = $editor->retrieve_container_biblio_record_entry_bucket($id) if $type eq 'biblio';
574 $bucket = $editor->retrieve_container_user_bucket($id) if $type eq 'user';
576 $evt = $editor->event unless $bucket;
577 return ($bucket, $evt);
580 sub fetch_container_item_e {
581 my( $self, $editor, $id, $type ) = @_;
584 $bucket = $editor->retrieve_container_copy_bucket_item($id) if $type eq 'copy';
585 $bucket = $editor->retrieve_container_call_number_bucket_item($id) if $type eq 'callnumber';
586 $bucket = $editor->retrieve_container_biblio_record_entry_bucket_item($id) if $type eq 'biblio';
587 $bucket = $editor->retrieve_container_user_bucket_item($id) if $type eq 'user';
589 $evt = $editor->event unless $bucket;
590 return ($bucket, $evt);
597 sub fetch_container_item {
598 my( $self, $id, $type ) = @_;
601 $logger->debug("Fetching container item $id with type $type");
603 my $meth = $buckets{$type} . "_item";
605 $bucket = $self->simplereq(
607 "open-ils.cstore.direct.container.$meth.retrieve", $id );
610 my $e = 'CONTAINER_CALL_NUMBER_BUCKET_ITEM_NOT_FOUND';
611 $e = 'CONTAINER_BIBLIO_RECORD_ENTRY_BUCKET_ITEM_NOT_FOUND' if $type eq 'biblio';
612 $e = 'CONTAINER_USER_BUCKET_ITEM_NOT_FOUND' if $type eq 'user';
613 $e = 'CONTAINER_COPY_BUCKET_ITEM_NOT_FOUND' if $type eq 'copy';
615 $evt = OpenILS::Event->new(
616 $e, itemid => $id, container_type => $type ) unless $bucket;
618 return ($bucket, $evt);
622 sub fetch_patron_standings {
624 $logger->debug("Fetching patron standings");
625 return $self->simplereq(
627 'open-ils.cstore.direct.config.standing.search.atomic', { id => { '!=' => undef } });
631 sub fetch_permission_group_tree {
633 $logger->debug("Fetching patron profiles");
634 return $self->simplereq(
636 'open-ils.actor.groups.tree.retrieve' );
640 sub fetch_patron_circ_summary {
641 my( $self, $userid ) = @_;
642 $logger->debug("Fetching patron summary for $userid");
643 my $summary = $self->simplereq(
645 "open-ils.storage.action.circulation.patron_summary", $userid );
649 $summary->[1] ||= 0.0;
656 sub fetch_copy_statuses {
658 $logger->debug("Fetching copy statuses");
659 return $self->simplereq(
661 'open-ils.cstore.direct.config.copy_status.search.atomic', { id => { '!=' => undef } });
664 sub fetch_copy_location {
665 my( $self, $id ) = @_;
667 my $cl = $self->cstorereq(
668 'open-ils.cstore.direct.asset.copy_location.retrieve', $id );
669 $evt = OpenILS::Event->new('ASSET_COPY_LOCATION_NOT_FOUND') unless $cl;
673 sub fetch_copy_locations {
675 return $self->simplereq(
677 'open-ils.cstore.direct.asset.copy_location.search.atomic', { id => { '!=' => undef } });
680 sub fetch_copy_location_by_name {
681 my( $self, $name, $org ) = @_;
683 my $cl = $self->cstorereq(
684 'open-ils.cstore.direct.asset.copy_location.search',
685 { name => $name, owning_lib => $org } );
686 $evt = OpenILS::Event->new('ASSET_COPY_LOCATION_NOT_FOUND') unless $cl;
690 sub fetch_callnumber {
691 my( $self, $id ) = @_;
694 my $e = OpenILS::Event->new( 'ASSET_CALL_NUMBER_NOT_FOUND', id => $id );
695 return( undef, $e ) unless $id;
697 $logger->debug("Fetching callnumber $id");
699 my $cn = $self->simplereq(
701 'open-ils.cstore.direct.asset.call_number.retrieve', $id );
702 $evt = $e unless $cn;
704 return ( $cn, $evt );
707 my %ORG_CACHE; # - these rarely change, so cache them..
709 my( $self, $id ) = @_;
710 return undef unless $id;
711 return $id if( ref($id) eq 'Fieldmapper::actor::org_unit' );
712 return $ORG_CACHE{$id} if $ORG_CACHE{$id};
713 $logger->debug("Fetching org unit $id");
716 my $org = $self->simplereq(
718 'open-ils.cstore.direct.actor.org_unit.retrieve', $id );
719 $evt = OpenILS::Event->new( 'ACTOR_ORG_UNIT_NOT_FOUND', id => $id ) unless $org;
720 $ORG_CACHE{$id} = $org;
726 my( $self, $type, $id ) = @_;
728 $logger->debug("Fetching $type stat cat: $id");
729 $cat = $self->simplereq(
731 "open-ils.cstore.direct.$type.stat_cat.retrieve", $id );
733 my $e = 'ASSET_STAT_CAT_NOT_FOUND';
734 $e = 'ACTOR_STAT_CAT_NOT_FOUND' if $type eq 'actor';
736 $evt = OpenILS::Event->new( $e, id => $id ) unless $cat;
737 return ( $cat, $evt );
740 sub fetch_stat_cat_entry {
741 my( $self, $type, $id ) = @_;
743 $logger->debug("Fetching $type stat cat entry: $id");
744 $entry = $self->simplereq(
746 "open-ils.cstore.direct.$type.stat_cat_entry.retrieve", $id );
748 my $e = 'ASSET_STAT_CAT_ENTRY_NOT_FOUND';
749 $e = 'ACTOR_STAT_CAT_ENTRY_NOT_FOUND' if $type eq 'actor';
751 $evt = OpenILS::Event->new( $e, id => $id ) unless $entry;
752 return ( $entry, $evt );
757 my( $self, $org_tree, $orgid ) = @_;
759 $logger->warn("find_org() did not receive a value for \$org_tree");
762 $logger->warn("find_org() did not receive a value for \$orgid");
765 return $org_tree if ( $org_tree->id eq $orgid );
766 return undef unless ref($org_tree->children);
767 for my $c (@{$org_tree->children}) {
768 my $o = $self->find_org($c, $orgid);
774 sub fetch_non_cat_type_by_name_and_org {
775 my( $self, $name, $orgId ) = @_;
776 $logger->debug("Fetching non cat type $name at org $orgId");
777 my $types = $self->simplereq(
779 'open-ils.cstore.direct.config.non_cataloged_type.search.atomic',
780 { name => $name, owning_lib => $orgId } );
781 return ($types->[0], undef) if($types and @$types);
782 return (undef, OpenILS::Event->new('CONFIG_NON_CATALOGED_TYPE_NOT_FOUND') );
785 sub fetch_non_cat_type {
786 my( $self, $id ) = @_;
787 $logger->debug("Fetching non cat type $id");
789 $type = $self->simplereq(
791 'open-ils.cstore.direct.config.non_cataloged_type.retrieve', $id );
792 $evt = OpenILS::Event->new('CONFIG_NON_CATALOGED_TYPE_NOT_FOUND') unless $type;
793 return ($type, $evt);
796 sub DB_UPDATE_FAILED {
797 my( $self, $payload ) = @_;
798 return OpenILS::Event->new('DATABASE_UPDATE_FAILED',
799 payload => ($payload) ? $payload : undef );
802 sub fetch_circ_duration_by_name {
803 my( $self, $name ) = @_;
805 $dur = $self->simplereq(
807 'open-ils.cstore.direct.config.rules.circ_duration.search.atomic', { name => $name } );
809 $evt = OpenILS::Event->new('CONFIG_RULES_CIRC_DURATION_NOT_FOUND') unless $dur;
813 sub fetch_recurring_fine_by_name {
814 my( $self, $name ) = @_;
816 $obj = $self->simplereq(
818 'open-ils.cstore.direct.config.rules.recuring_fine.search.atomic', { name => $name } );
820 $evt = OpenILS::Event->new('CONFIG_RULES_RECURING_FINE_NOT_FOUND') unless $obj;
824 sub fetch_max_fine_by_name {
825 my( $self, $name ) = @_;
827 $obj = $self->simplereq(
829 'open-ils.cstore.direct.config.rules.max_fine.search.atomic', { name => $name } );
831 $evt = OpenILS::Event->new('CONFIG_RULES_MAX_FINE_NOT_FOUND') unless $obj;
836 my( $self, $method, @params ) = @_;
837 return $self->simplereq(
838 'open-ils.storage', $method, @params );
842 my( $self, $method, @params ) = @_;
843 return $self->simplereq(
844 'open-ils.cstore', $method, @params );
848 my( $self, $e, $name ) = @_;
849 if( $e and ref($e) eq 'HASH' and
850 defined($e->{textcode}) and $e->{textcode} eq $name ) {
857 my( undef, $f, $l ) = caller(0);
858 my( undef, undef, undef, $s ) = caller(1);
861 $logger->debug("LOGMARK: $f:$l:$s");
865 sub fetch_open_circulation {
866 my( $self, $cid ) = @_;
869 my $circ = $self->cstorereq(
870 'open-ils.cstore.direct.action.open_circulation.search',
871 { target_copy => $cid, stop_fines_time => undef } );
872 $evt = OpenILS::Event->new('ACTION_CIRCULATION_NOT_FOUND') unless $circ;
873 return ($circ, $evt);
876 sub fetch_all_open_circulation {
877 my( $self, $cid ) = @_;
880 my $circ = $self->cstorereq(
881 'open-ils.cstore.direct.action.open_circulation.search',
882 { target_copy => $cid, xact_finish => undef } );
883 $evt = OpenILS::Event->new('ACTION_CIRCULATION_NOT_FOUND') unless $circ;
884 return ($circ, $evt);
888 sub copy_status_from_name {
889 my( $self, $name ) = @_;
890 $copy_statuses = $self->fetch_copy_statuses unless $copy_statuses;
891 for my $status (@$copy_statuses) {
892 return $status if( $status->name =~ /$name/i );
897 sub copy_status_to_name {
898 my( $self, $sid ) = @_;
899 $copy_statuses = $self->fetch_copy_statuses unless $copy_statuses;
900 for my $status (@$copy_statuses) {
901 return $status->name if( $status->id == $sid );
908 my( $self, $arg ) = @_;
909 return $arg if ref $arg;
910 $copy_statuses = $self->fetch_copy_statuses unless $copy_statuses;
911 my ($stat) = grep { $_->id == $arg } @$copy_statuses;
915 sub fetch_open_transit_by_copy {
916 my( $self, $copyid ) = @_;
918 $transit = $self->cstorereq(
919 'open-ils.cstore.direct.action.transit_copy.search',
920 { target_copy => $copyid, dest_recv_time => undef });
921 $evt = OpenILS::Event->new('ACTION_TRANSIT_COPY_NOT_FOUND') unless $transit;
922 return ($transit, $evt);
926 my( $self, $copy ) = @_;
927 return undef unless $copy;
928 $copy->status( $copy->status->id ) if ref($copy->status);
929 $copy->location( $copy->location->id ) if ref($copy->location);
930 $copy->circ_lib( $copy->circ_lib->id ) if ref($copy->circ_lib);
934 # un-fleshes a copy and updates it in the DB
935 # returns a DB_UPDATE_FAILED event on error
936 # returns undef on success
938 my( $self, %params ) = @_;
940 my $copy = $params{copy} || die "update_copy(): copy required";
941 my $editor = $params{editor} || die "update_copy(): copy editor required";
942 my $session = $params{session};
944 $logger->debug("Updating copy in the database: " . $copy->id);
946 $self->unflesh_copy($copy);
947 $copy->editor( $editor );
948 $copy->edit_date( 'now' );
951 my $meth = 'open-ils.storage.direct.asset.copy.update';
953 $s = $session->request( $meth, $copy )->gather(1) if $session;
954 $s = $self->storagereq( $meth, $copy ) unless $session;
956 $logger->debug("Update of copy ".$copy->id." returned: $s");
958 return $self->DB_UPDATE_FAILED($copy) unless $s;
962 sub fetch_billable_xact {
963 my( $self, $id ) = @_;
965 $logger->debug("Fetching billable transaction %id");
966 $xact = $self->cstorereq(
967 'open-ils.cstore.direct.money.billable_transaction.retrieve', $id );
968 $evt = OpenILS::Event->new('MONEY_BILLABLE_TRANSACTION_NOT_FOUND') unless $xact;
969 return ($xact, $evt);
972 sub fetch_billable_xact_summary {
973 my( $self, $id ) = @_;
975 $logger->debug("Fetching billable transaction summary %id");
976 $xact = $self->cstorereq(
977 'open-ils.cstore.direct.money.billable_transaction_summary.retrieve', $id );
978 $evt = OpenILS::Event->new('MONEY_BILLABLE_TRANSACTION_NOT_FOUND') unless $xact;
979 return ($xact, $evt);
982 sub fetch_fleshed_copy {
983 my( $self, $id ) = @_;
985 $logger->info("Fetching fleshed copy $id");
986 $copy = $self->cstorereq(
987 "open-ils.cstore.direct.asset.copy.retrieve", $id,
989 flesh_fields => { acp => [ qw/ circ_lib location status stat_cat_entries / ] }
992 $evt = OpenILS::Event->new('ASSET_COPY_NOT_FOUND', id => $id) unless $copy;
993 return ($copy, $evt);
997 # returns the org that owns the callnumber that the copy
999 sub fetch_copy_owner {
1000 my( $self, $copyid ) = @_;
1001 my( $copy, $cn, $evt );
1002 $logger->debug("Fetching copy owner $copyid");
1003 ($copy, $evt) = $self->fetch_copy($copyid);
1004 return (undef,$evt) if $evt;
1005 ($cn, $evt) = $self->fetch_callnumber($copy->call_number);
1006 return (undef,$evt) if $evt;
1007 return ($cn->owning_lib);
1010 sub fetch_copy_note {
1011 my( $self, $id ) = @_;
1013 $logger->debug("Fetching copy note $id");
1014 $note = $self->cstorereq(
1015 'open-ils.cstore.direct.asset.copy_note.retrieve', $id );
1016 $evt = OpenILS::Event->new('ASSET_COPY_NOTE_NOT_FOUND', id => $id ) unless $note;
1017 return ($note, $evt);
1020 sub fetch_call_numbers_by_title {
1021 my( $self, $titleid ) = @_;
1022 $logger->info("Fetching call numbers by title $titleid");
1023 return $self->cstorereq(
1024 'open-ils.cstore.direct.asset.call_number.search.atomic',
1025 { record => $titleid, deleted => 'f' });
1026 #'open-ils.storage.direct.asset.call_number.search.record.atomic', $titleid);
1029 sub fetch_copies_by_call_number {
1030 my( $self, $cnid ) = @_;
1031 $logger->info("Fetching copies by call number $cnid");
1032 return $self->cstorereq(
1033 'open-ils.cstore.direct.asset.copy.search.atomic', { call_number => $cnid, deleted => 'f' } );
1034 #'open-ils.storage.direct.asset.copy.search.call_number.atomic', $cnid );
1037 sub fetch_user_by_barcode {
1038 my( $self, $bc ) = @_;
1039 my $cardid = $self->cstorereq(
1040 'open-ils.cstore.direct.actor.card.id_list', { barcode => $bc } );
1041 return (undef, OpenILS::Event->new('ACTOR_CARD_NOT_FOUND', barcode => $bc)) unless $cardid;
1042 my $user = $self->cstorereq(
1043 'open-ils.cstore.direct.actor.user.search', { card => $cardid } );
1044 return (undef, OpenILS::Event->new('ACTOR_USER_NOT_FOUND', card => $cardid)) unless $user;
1050 # ---------------------------------------------------------------------
1051 # Updates and returns the patron penalties
1052 # ---------------------------------------------------------------------
1053 sub update_patron_penalties {
1054 my( $self, %args ) = @_;
1055 return $self->simplereq(
1057 'open-ils.penalty.patron_penalty.calculate',
1058 { update => 1, %args }
1063 my( $self, $billid ) = @_;
1064 $logger->debug("Fetching billing $billid");
1065 my $bill = $self->cstorereq(
1066 'open-ils.cstore.direct.money.billing.retrieve', $billid );
1067 my $evt = OpenILS::Event->new('MONEY_BILLING_NOT_FOUND') unless $bill;
1068 return($bill, $evt);
1074 sub fetch_org_tree {
1076 return $ORG_TREE if $ORG_TREE;
1077 return $ORG_TREE = OpenILS::Utils::CStoreEditor->new->search_actor_org_unit(
1079 {"parent_ou" => undef },
1082 flesh_fields => { aou => ['children'] },
1083 order_by => { aou => 'name'}
1090 my( $self, $node, $callback ) = @_;
1091 return unless $node;
1093 if( $node->children ) {
1094 $self->walk_org_tree($_, $callback) for @{$node->children};
1099 my( $self, $item ) = @_;
1100 return 1 if $item and $item !~ /^f$/i;
1105 # This logic now lives in storage
1106 sub __patron_money_owed {
1107 my( $self, $patronid ) = @_;
1108 my $ses = OpenSRF::AppSession->create('open-ils.storage');
1109 my $req = $ses->request(
1110 'open-ils.storage.money.billable_transaction.summary.search',
1111 { usr => $patronid, xact_finish => undef } );
1115 while( $data = $req->recv ) {
1116 $data = $data->content;
1117 $total += $data->balance_owed;
1122 sub patron_money_owed {
1123 my( $self, $userid ) = @_;
1124 return $self->storagereq(
1125 'open-ils.storage.actor.user.total_owed', $userid);
1128 sub patron_total_items_out {
1129 my( $self, $userid ) = @_;
1130 return $self->storagereq(
1131 'open-ils.storage.actor.user.total_out', $userid);
1137 #---------------------------------------------------------------------
1138 # Returns ($summary, $event)
1139 #---------------------------------------------------------------------
1143 my $editor = shift || OpenILS::Utils::CStoreEditor->new;
1145 $id = $id->id if (ref($id));
1147 my $xact = $editor->retrieve_money_billable_transaction(
1151 flesh_fields => { mbt => [ qw/billings payments grocery circulation/ ] }
1154 ) or return (undef, $editor->event);
1156 return $self->make_mbts($xact);
1160 #---------------------------------------------------------------------
1161 # Given a list of money.billable_transaction objects, this creates
1162 # transaction summary objects for each
1163 #--------------------------------------------------------------------
1169 for my $x (@xacts) {
1171 my $s = new Fieldmapper::money::billable_transaction_summary;
1175 $s->xact_start( $x->xact_start );
1176 $s->xact_finish( $x->xact_finish );
1180 for my $b (@{ $x->billings }) {
1181 next if ($self->is_true($b->voided));
1182 $to += ($b->amount * 100);
1183 $lb ||= $b->billing_ts;
1184 if ($b->billing_ts ge $lb) {
1185 $lb = $b->billing_ts;
1186 $s->last_billing_note($b->note);
1187 $s->last_billing_ts($b->billing_ts);
1188 $s->last_billing_type($b->billing_type);
1192 $s->total_owed( sprintf('%0.2f', $to / 100 ) );
1196 for my $p (@{ $x->payments }) {
1197 next if ($self->is_true($p->voided));
1198 $tp += ($p->amount * 100);
1199 $lp ||= $p->payment_ts;
1200 if ($p->payment_ts ge $lp) {
1201 $lp = $p->payment_ts;
1202 $s->last_payment_note($p->note);
1203 $s->last_payment_ts($p->payment_ts);
1204 $s->last_payment_type($p->payment_type);
1208 $s->total_paid( sprintf('%0.2f', $tp / 100 ) );
1209 $s->balance_owed( sprintf('%0.2f', ($to - $tp) / 100) );
1210 $s->xact_type('grocery') if ($x->grocery);
1211 $s->xact_type('circulation') if ($x->circulation);
1213 $logger->debug("Created mbts with balance_owed = ". $s->balance_owed);
1222 sub ou_ancestor_setting_value {
1223 my $obj = ou_ancestor_setting(@_);
1224 return ($obj) ? $obj->{value} : undef;
1227 sub ou_ancestor_setting {
1228 my( $self, $orgid, $name, $e ) = @_;
1229 $e = $e || OpenILS::Utils::CStoreEditor->new;
1232 my $setting = $e->search_actor_org_unit_setting({org_unit=>$orgid, name=>$name})->[0];
1235 $logger->info("found org_setting $name at org $orgid : " . $setting->value);
1236 return { org => $orgid, value => OpenSRF::Utils::JSON->JSON2perl($setting->value) };
1239 my $org = $e->retrieve_actor_org_unit($orgid) or return $e->event;
1240 $orgid = $org->parent_ou or return undef;
1248 # returns the ISO8601 string representation of the requested epoch in GMT
1250 my( $self, $epoch ) = @_;
1251 my ($sec,$min,$hour,$mday,$mon,$year) = gmtime($epoch);
1252 $year += 1900; $mon += 1;
1254 '%s-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d-00',
1255 $year, $mon, $mday, $hour, $min, $sec);