1 package OpenILS::Application::Collections;
2 use strict; use warnings;
3 use OpenSRF::EX qw(:try);
4 use OpenILS::Application::AppUtils;
5 use OpenSRF::Utils::Logger qw(:logger);
6 use OpenSRF::Utils qw/:datetime/;
7 use OpenILS::Application;
8 use OpenILS::Utils::Fieldmapper;
9 use base 'OpenILS::Application';
10 use OpenILS::Utils::CStoreEditor qw/:funcs/;
12 use OpenILS::Const qw/:const/;
13 my $U = "OpenILS::Application::AppUtils";
15 use Scalar::Util 'blessed';
21 # --------------------------------------------------------------
22 # Loads the config info
23 # --------------------------------------------------------------
24 sub initialize { return 1; }
26 __PACKAGE__->register_method(
27 method => 'user_from_bc',
28 api_name => 'open-ils.collections.user_id_from_barcode',
32 my( $self, $conn, $auth, $bc ) = @_;
33 my $e = new_editor(authtoken=>$auth);
34 return $e->event unless $e->checkauth;
35 return $e->event unless $e->allowed('VIEW_USER');
36 my $card = $e->search_actor_card({barcode=>$bc})->[0]
38 my $user = $e->retrieve_actor_user($card->usr)
44 __PACKAGE__->register_method(
45 method => 'users_of_interest',
46 api_name => 'open-ils.collections.users_of_interest.retrieve',
52 Returns an array of user information objects that the system
53 based on the search criteria provided. If the total fines
54 a user owes reaches or exceeds "fine_level" on or befre "age"
55 and the fines were created at "location", the user will be
56 included in the return set/,
60 desc => 'The authentication token',
64 desc => q/Number of days back to check/,
68 { name => 'fine_level',
69 desc => q/The fine threshold at which users will be included in the search results /,
73 desc => q/The short-name of the orginization unit (library) at which the fines were created.
74 If a selected location has 'child' locations (e.g. a library region), the
75 child locations will be included in the search/,
81 desc => q/An array of user information objects.
82 usr : Array of user information objects containing id, dob, profile, and groups
83 threshold_amount : The total amount the patron owes that is at least as old
84 as the fine "age" and whose transaction was created at the searched location
85 last_pertinent_billing : The time of the last billing that relates to this query
93 groups => [ 'Patron', 'Staff' ],
95 threshold_amount => 99,
102 sub users_of_interest {
103 my( $self, $conn, $auth, $age, $fine_level, $location ) = @_;
105 return OpenILS::Event->new('BAD_PARAMS')
106 unless ($auth and $age and $location);
108 my $e = new_editor(authtoken => $auth);
109 return $e->event unless $e->checkauth;
111 my $org = $e->search_actor_org_unit({shortname => $location})
112 or return $e->event; $org = $org->[0];
114 # they need global perms to view users so no org is provided
115 return $e->event unless $e->allowed('VIEW_USER');
119 my $ses = OpenSRF::AppSession->create('open-ils.storage');
122 my $req = $ses->request(
123 'open-ils.storage.money.collections.users_of_interest',
124 $age, $fine_level, $location);
126 # let the client know we're still here
127 $conn->status( new OpenSRF::DomainObject::oilsContinueStatus );
129 return process_users_of_interest_results(
130 $self, $conn, $e, $req, $start, $age, $fine_level, $location);
134 __PACKAGE__->register_method(
135 method => 'users_of_interest_warning_penalty',
136 api_name => 'open-ils.collections.users_of_interest.warning_penalty.retrieve',
142 Returns an array of user information objects for users that have the
143 PATRON_EXCEEDS_COLLECTIONS_WARNING penalty applied,
144 based on the search criteria provided./,
148 desc => 'The authentication token',
152 desc => q/The short-name of the orginization unit (library) at which the penalty is applied.
153 If a selected location has 'child' locations (e.g. a library region), the
154 child locations will be included in the search/,
158 desc => q/Optional. Minimum age of the penalty application/,
159 type => q/interval, e.g "30 days"/,
162 desc => q/Optional. Maximum age of the penalty application/,
163 type => q/interval, e.g "90 days"/,
168 desc => q/An array of user information objects.
169 usr : Array of user information objects containing id, dob, profile, and groups
170 threshold_amount : The total amount the patron owes that is at least as old
171 as the fine "age" and whose transaction was created at the searched location
172 last_pertinent_billing : The time of the last billing that relates to this query
180 groups => [ 'Patron', 'Staff' ],
182 threshold_amount => 99, # TODO: still needed?
190 sub users_of_interest_warning_penalty {
191 my( $self, $conn, $auth, $location, $min_age, $max_age ) = @_;
193 return OpenILS::Event->new('BAD_PARAMS') unless ($auth and $location);
195 my $e = new_editor(authtoken => $auth);
196 return $e->event unless $e->checkauth;
198 my $org = $e->search_actor_org_unit({shortname => $location})
199 or return $e->event; $org = $org->[0];
201 # they need global perms to view users so no org is provided
202 return $e->event unless $e->allowed('VIEW_USER');
204 my $org_ids = $e->json_query({from => ['actor.org_unit_full_path', $org->id]});
206 my $ses = OpenSRF::AppSession->create('open-ils.cstore');
209 my $max_set_date = DateTime->now->subtract(seconds =>
210 interval_to_seconds($max_age))->strftime( '%F %T%z' ) if $max_age;
211 my $min_set_date = DateTime->now->subtract(seconds =>
212 interval_to_seconds($min_age))->strftime( '%F %T%z' ) if $min_age;
216 select => {ausp => ['usr']},
223 filter => {name => 'circ.collections.exempt'}
231 standing_penalty => 4, # PATRON_EXCEEDS_COLLECTIONS_WARNING
232 org_unit => [ map {$_->{id}} @$org_ids ],
234 {stop_date => undef},
235 {stop_date => {'>' => 'now'}}
238 # We are only interested in users that do not have the
239 # circ.collections.exempt setting applied
240 '+aus' => {value => undef}
244 $query->{where}->{'-and'} = [] if $max_set_date or $min_set_date;
245 push(@{$query->{where}->{'-and'}}, {set_date => {'>' => $max_set_date}}) if $max_set_date;
246 push(@{$query->{where}->{'-and'}}, {set_date => {'<' => $min_set_date}}) if $min_set_date;
248 my $req = $ses->request('open-ils.cstore.json_query', $query);
250 # let the client know we're still here
251 $conn->status( new OpenSRF::DomainObject::oilsContinueStatus );
253 return process_users_of_interest_results(
254 $self, $conn, $e, $req, $start, $min_age, '', $location, $max_age);
260 sub process_users_of_interest_results {
261 my($self, $conn, $e, $req, $starttime, @params) = @_;
264 while( my $resp = $req->recv(timeout => 7200) ) {
266 return $req->failed if $req->failed;
267 my $hash = $resp->content;
271 $total = time - $starttime;
272 $logger->info("collections: request (@params) took $total seconds");
275 my $u = $e->retrieve_actor_user(
280 flesh_fields => {au => ["groups","profile", "card"]},
283 ) or return $e->event;
288 profile => $u->profile->name,
289 barcode => $u->card->barcode,
290 groups => [ map { $_->name } @{$u->groups} ],
293 $conn->respond($hash);
300 __PACKAGE__->register_method(
301 method => 'users_owing_money',
302 api_name => 'open-ils.collections.users_owing_money.retrieve',
308 Returns an array of users that owe money during
309 the given time frame at the location (or child locations)
314 desc => 'The authentication token',
317 { name => 'start_date',
318 desc => 'The start of the time interval to check',
319 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
322 { name => 'end_date',
323 desc => q/Then end date of the time interval to check/,
324 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
326 { name => 'fine_level',
327 desc => q/The fine threshold at which users will be included in the search results /,
330 { name => 'locations',
331 desc => q/ A list of one or more org-unit short names.
332 If a selected location has 'child' locations (e.g. a library region), the
333 child locations will be included in the search/,
338 desc => q/An array of user information objects/,
345 sub users_owing_money {
346 my( $self, $conn, $auth, $start_date, $end_date, $fine_level, @locations ) = @_;
348 return OpenILS::Event->new('BAD_PARAMS')
349 unless ($auth and $start_date and $end_date and @locations);
351 my $e = new_editor(authtoken => $auth);
352 return $e->event unless $e->checkauth;
354 # they need global perms to view users so no org is provided
355 return $e->event unless $e->allowed('VIEW_USER');
359 my $ses = OpenSRF::AppSession->create('open-ils.storage');
362 my $req = $ses->request(
363 'open-ils.storage.money.collections.users_owing_money',
364 $start_date, $end_date, $fine_level, @locations);
366 # let the client know we're still here
367 $conn->status( new OpenSRF::DomainObject::oilsContinueStatus );
369 return process_users_of_interest_results(
370 $self, $conn, $e, $req, $start, $start_date, $end_date, $fine_level, @locations);
375 __PACKAGE__->register_method(
376 method => 'users_with_activity',
377 api_name => 'open-ils.collections.users_with_activity.retrieve',
383 Returns an array of users that are already in collections
384 and had any type of billing or payment activity within
385 the given time frame at the location (or child locations)
390 desc => 'The authentication token',
393 { name => 'start_date',
394 desc => 'The start of the time interval to check',
395 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
398 { name => 'end_date',
399 desc => q/Then end date of the time interval to check/,
400 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
402 { name => 'location',
403 desc => q/The short-name of the orginization unit (library) at which the activity occurred.
404 If a selected location has 'child' locations (e.g. a library region), the
405 child locations will be included in the search/,
411 desc => q/An array of user information objects/,
417 sub users_with_activity {
418 my( $self, $conn, $auth, $start_date, $end_date, $location ) = @_;
419 return OpenILS::Event->new('BAD_PARAMS')
420 unless ($auth and $start_date and $end_date and $location);
422 my $e = new_editor(authtoken => $auth);
423 return $e->event unless $e->checkauth;
425 my $org = $e->search_actor_org_unit({shortname => $location})
426 or return $e->event; $org = $org->[0];
427 return $e->event unless $e->allowed('VIEW_USER', $org->id);
429 my $ses = OpenSRF::AppSession->create('open-ils.storage');
432 my $req = $ses->request(
433 'open-ils.storage.money.collections.users_with_activity.atomic',
434 $start_date, $end_date, $location);
436 $conn->status( new OpenSRF::DomainObject::oilsContinueStatus );
439 while( my $resp = $req->recv(timeout => 7200) ) {
442 $total = time - $start;
443 $logger->info("collections: users_with_activity search ".
444 "($start_date, $end_date, $location) took $total seconds");
447 return $req->failed if $req->failed;
448 $conn->respond($resp->content);
456 __PACKAGE__->register_method(
457 method => 'put_into_collections',
458 api_name => 'open-ils.collections.put_into_collections',
463 Marks a user as being "in collections" at a given location
468 desc => 'The authentication token',
472 desc => 'The id of the user to plact into collections',
476 { name => 'location',
477 desc => q/The short-name of the orginization unit (library)
478 for which the user is being placed in collections/,
481 { name => 'fee_amount',
483 The amount of money that a patron should be fined.
484 If this field is empty, no fine is created.
488 { name => 'fee_note',
490 Custom note that is added to the the billing.
491 This field is not required.
492 Note: fee_note is not the billing_type. Billing_type type is
493 decided by the system. (e.g. "fee for collections").
494 fee_note is purely used for any additional needed information
495 and is only visible to staff.
502 desc => q/A SUCCESS event on success, error event on failure/,
507 sub put_into_collections {
508 my( $self, $conn, $auth, $user_id, $location, $fee_amount, $fee_note ) = @_;
510 return OpenILS::Event->new('BAD_PARAMS')
511 unless ($auth and $user_id and $location);
513 my $e = new_editor(authtoken => $auth, xact =>1);
514 return $e->event unless $e->checkauth;
516 my $org = $e->search_actor_org_unit({shortname => $location});
517 return $e->event unless $org = $org->[0];
518 return $e->event unless $e->allowed('money.collections_tracker.create', $org->id);
520 my $existing = $e->search_money_collections_tracker(
522 location => $org->id,
524 collector => $e->requestor->id
529 return OpenILS::Event->new('MONEY_COLLECTIONS_TRACKER_EXISTS') if @$existing;
531 $logger->info("collect: user ".$e->requestor->id.
532 " putting user $user_id into collections for $location");
534 my $tracker = Fieldmapper::money::collections_tracker->new;
536 $tracker->usr($user_id);
537 $tracker->collector($e->requestor->id);
538 $tracker->location($org->id);
539 $tracker->enter_time('now');
541 $e->create_money_collections_tracker($tracker)
545 my $evt = add_collections_fee($e, $user_id, $org, $fee_amount, $fee_note );
551 my $pen = Fieldmapper::actor::user_standing_penalty->new;
552 $pen->org_unit($org->id);
554 $pen->standing_penalty(30); # PATRON_IN_COLLECTIONS
555 $pen->staff($e->requestor->id);
556 $pen->note($fee_note) if $fee_note;
557 $U->simplereq('open-ils.actor', 'open-ils.actor.user.penalty.apply', $auth, $pen);
559 return OpenILS::Event->new('SUCCESS');
562 sub add_collections_fee {
563 my( $e, $patron_id, $org, $fee_amount, $fee_note ) = @_;
567 $logger->info("collect: adding fee to user $patron_id : $fee_amount : $fee_note");
569 my $xact = Fieldmapper::money::grocery->new;
570 $xact->usr($patron_id);
571 $xact->xact_start('now');
572 $xact->billing_location($org->id);
574 $xact = $e->create_money_grocery($xact) or return $e->event;
576 my $bill = Fieldmapper::money::billing->new;
577 $bill->note($fee_note);
578 $bill->xact($xact->id);
580 $bill->billing_type(OILS_BILLING_TYPE_COLLECTION_FEE);
581 $bill->amount($fee_amount);
583 $e->create_money_billing($bill) or return $e->event;
590 __PACKAGE__->register_method(
591 method => 'remove_from_collections',
592 api_name => 'open-ils.collections.remove_from_collections',
594 Returns the users that are currently in collections and
595 had activity during the provided interval. Dates are inclusive.
596 @param start_date The beginning of the activity interval
597 @param end_date The end of the activity interval
598 @param location The location at which the fines were created
603 __PACKAGE__->register_method(
604 method => 'remove_from_collections',
605 api_name => 'open-ils.collections.remove_from_collections',
610 Removes a user from the collections table for the given location
615 desc => 'The authentication token',
619 desc => 'The id of the user to plact into collections',
623 { name => 'location',
624 desc => q/The short-name of the orginization unit (library)
625 for which the user is being removed from collections/,
631 desc => q/A SUCCESS event on success, error event on failure/,
637 sub remove_from_collections {
638 my( $self, $conn, $auth, $user_id, $location ) = @_;
640 return OpenILS::Event->new('BAD_PARAMS')
641 unless ($auth and $user_id and $location);
643 my $e = new_editor(authtoken => $auth, xact=>1);
644 return $e->event unless $e->checkauth;
646 my $org = $e->search_actor_org_unit({shortname => $location})
647 or return $e->event; $org = $org->[0];
648 return $e->event unless $e->allowed('money.collections_tracker.delete', $org->id);
650 my $tracker = $e->search_money_collections_tracker(
651 { usr => $user_id, location => $org->id })
654 $e->delete_money_collections_tracker($tracker->[0])
658 return OpenILS::Event->new('SUCCESS');
662 #__PACKAGE__->register_method(
663 # method => 'transaction_details',
664 # api_name => 'open-ils.collections.user_transaction_details.retrieve',
670 __PACKAGE__->register_method(
671 method => 'transaction_details',
672 api_name => 'open-ils.collections.user_transaction_details.retrieve',
677 Returns a list of fleshed user objects with transaction details
682 desc => 'The authentication token',
685 { name => 'start_date',
686 desc => 'The start of the time interval to check',
687 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
690 { name => 'end_date',
691 desc => q/Then end date of the time interval to check/,
692 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
694 { name => 'location',
695 desc => q/The short-name of the orginization unit (library) at which the activity occurred.
696 If a selected location has 'child' locations (e.g. a library region), the
697 child locations will be included in the search/,
702 desc => 'An array of user ids',
708 desc => q/A list of objects. Object keys include:
710 transactions : An object with keys :
711 circulations : Fleshed circulation objects
712 grocery : Fleshed 'grocery' transaction objects
719 sub transaction_details {
720 my( $self, $conn, $auth, $start_date, $end_date, $location, $user_list ) = @_;
722 return OpenILS::Event->new('BAD_PARAMS')
723 unless ($auth and $start_date and $end_date and $location and $user_list);
725 my $e = new_editor(authtoken => $auth);
726 return $e->event unless $e->checkauth;
728 # they need global perms to view users so no org is provided
729 return $e->event unless $e->allowed('VIEW_USER');
731 my $org = $e->search_actor_org_unit({shortname => $location})
732 or return $e->event; $org = $org->[0];
734 # get a reference to the org inside of the tree
735 $org = $U->find_org($U->get_org_tree(), $org->id);
738 for my $uid (@$user_list) {
741 $blob->{usr} = $e->retrieve_actor_user(
750 "standing_penalties",
761 $blob->{transactions} = {
763 fetch_circ_xacts($e, $uid, $org, $start_date, $end_date),
765 fetch_grocery_xacts($e, $uid, $org, $start_date, $end_date),
767 fetch_reservation_xacts($e, $uid, $org, $start_date, $end_date)
770 # for each transaction, flesh the workstatoin on any attached payment
771 # and make the payment object a real object (e.g. cash payment),
772 # not just a generic payment object
774 @{$blob->{transactions}->{circulations}},
775 @{$blob->{transactions}->{reservations}},
776 @{$blob->{transactions}->{grocery}} ) {
779 if( $ps = $xact->payments and @$ps ) {
780 my @fleshed; my $evt;
782 ($p, $evt) = flesh_payment($e,$p);
786 $xact->payments(\@fleshed);
790 push( @data, $blob );
796 __PACKAGE__->register_method(
797 method => 'user_balance_summary',
798 api_name => 'open-ils.collections.user_balance_summary.generate',
803 desc => q/Collect balance information for users in collections. By default,
804 only the total balance owed is calculated. Use the "include_xacts"
805 param to include per-transaction summaries as well./,
808 desc => 'The authentication token',
812 Hash of API arguments. Options include:
813 location -- org unit shortname
814 start_date -- ISO 8601 date. limit to patrons added to collections on or after this date (optional).
815 end_date -- ISO 8601 date. limit to patrons added to collections on or before this date (optional).
816 user_id -- retrieve information only for this user (takes preference over
817 start and end_date). May be a single ID or list of IDs. (optional).
818 include_xacts -- If true, include a summary object per transaction in addition to the full balance owed
825 The file name prefix of the file to be created.
826 The file name format will be:
827 user_balance_YYYY-MM-DD_${location}_${start_date}_${end_date}_${user_id}.[tmp|xml]
828 Optional params not provided by the caller will not be part of the file name.
830 user_balance_BR1_2012-05-25_2012-01-01_2012-12-31 # start and end dates
831 user_balance_BR2_2012-05-25_153244 # user id only.
832 In-process files will have a .tmp suffix
833 Completed files will have a .xml suffix
840 sub user_balance_summary {
841 my ($self, $client, $auth, $args) = @_;
843 my $location = $$args{location};
844 my $start_date = $$args{start_date};
845 my $end_date = $$args{end_date};
846 my $user_id = $$args{user_id};
848 return OpenILS::Event->new('BAD_PARAMS')
849 unless $auth and $location and
850 ($start_date or $end_date or $user_id);
852 my $e = new_editor(authtoken => $auth);
853 return $e->event unless $e->checkauth;
855 my $org = $e->search_actor_org_unit({shortname => $location})->[0]
858 # they need global perms to view users so no org is provided
859 return $e->event unless $e->allowed('VIEW_USER', $org->id);
861 my $org_list = $U->get_org_descendants($org->id);
863 my ($evt, $file_prefix, $file_name, $FILE) = setup_batch_file('user_balance', $args);
865 $client->respond_complete($evt || $file_prefix);
870 @user_list = (ref $user_id eq 'ARRAY') ? @$user_id : ($user_id);
873 # collect the users from the tracker table based on the provided filters
876 select => {mct => ['usr']},
878 where => {location => $org_list}
881 $query->{where}->{enter_time} = {'>=' => $start_date};
882 $query->{where}->{enter_time} = {'<=' => $end_date};
883 my $users = $e->json_query($query);
884 @user_list = map {$_->{usr}} @$users;
887 print $FILE "<Collections>\n"; # append to the document as we have data
889 for my $user_id (@user_list) {
890 my $user_doc = XML::LibXML::Document->new;
891 my $root = $user_doc->createElement('User');
892 $user_doc->setDocumentElement($root);
894 my $user = $e->retrieve_actor_user([
901 'standing_penalties',
910 my $au_doc = $user->toXML({no_virt => 1, skip_fields => {au => ['passwd']}});
911 my $au_node = $au_doc->documentElement;
912 $user_doc->adoptNode($au_node);
913 $root->appendChild($au_node);
915 my $circ_ids = $e->search_action_circulation(
916 {usr => $user_id, circ_lib => $org_list, xact_finish => undef},
920 my $groc_ids = $e->search_money_grocery(
921 {usr => $user_id, billing_location => $org_list, xact_finish => undef},
925 my $res_ids = $e->search_booking_reservation(
926 {usr => $user_id, pickup_lib => $org_list, xact_finish => undef},
930 # get the sum owed an all transactions
931 my $balance = $e->json_query({
933 { column => 'balance_owed',
939 where => {id => [@$circ_ids, @$groc_ids, @$res_ids]}
942 $balance = $balance ? $balance->{balance_owed} : '0';
944 my $xacts_node = $user_doc->createElement('Transactions');
945 my $balance_node = $user_doc->createElement('BalanceOwed');
946 $balance_node->appendChild($user_doc->createTextNode($balance));
947 $xacts_node->appendChild($balance_node);
948 $root->appendChild($xacts_node);
950 if ($$args{include_xacts}) {
951 my $xacts = $e->search_money_billable_transaction_summary(
952 {id => [@$circ_ids, @$groc_ids, @$res_ids]},
956 for my $xact (@$xacts) {
957 my $xact_node = $xact->toXML({no_virt => 1})->documentElement;
958 $user_doc->adoptNode($xact_node);
959 $xacts_node->appendChild($xact_node);
963 print $FILE $user_doc->documentElement->toString(1) . "\n";
966 print $FILE "\n</Collections>";
969 (my $complete_file = $file_name) =~ s|.tmp$|.xml|og;
971 unless (move($file_name, $complete_file)) {
972 $logger->error("collections: unable to move ".
973 "user_balance file $file_name => $complete_file : $@");
979 sub setup_batch_file {
982 my $location = $$args{location};
983 my $start_date = $$args{start_date};
984 my $end_date = $$args{end_date};
985 my $user_id = $$args{user_id};
987 my $conf = OpenSRF::Utils::SettingsClient->new;
988 my $dir_name = $conf->config_value(apps =>
989 'open-ils.collections' => app_settings => 'batch_file_dir');
992 $logger->error("collections: no batch_file_dir directory configured");
993 return OpenILS::Event->new('COLLECTIONS_FILE_ERROR');
996 unless (-e $dir_name) {
997 eval { mkpath($dir_name); };
999 $logger->error("collections: unable to create batch_file_dir directory $dir_name : $@");
1000 return OpenILS::Event->new('COLLECTIONS_FILE_ERROR');
1004 my $file_prefix = "${prefix}_" . DateTime->now->strftime('%F') . "_$location";
1005 $file_prefix .= "_$start_date" if $start_date;
1006 $file_prefix .= "_$end_date" if $end_date;
1007 $file_prefix .= "_$user_id" if $user_id;
1010 my $file_name = File::Spec->catfile($dir_name, "$file_prefix.tmp");
1012 unless (open($FILE, '>', $file_name)) {
1013 $logger->error("collections: unable to open user_balance_summary file $file_name : $@");
1014 return OpenILS::Event->new('COLLECTIONS_FILE_ERROR');
1017 return (undef, $file_prefix, $file_name, $FILE);
1023 my $type = $p->payment_type;
1024 $logger->debug("collect: fleshing workstation on payment $type : ".$p->id);
1025 my $meth = "retrieve_money_$type";
1026 $p = $e->$meth($p->id) or return (undef, $e->event);
1028 $p->payment_type($type);
1030 $e->retrieve_actor_workstation(
1035 flesh_fields => { aws => [ 'owning_lib' ] }
1040 } catch Error with {};
1045 # --------------------------------------------------------------
1046 # Collect all open circs for the user
1047 # For each circ, see if any billings or payments were created
1048 # during the given time period.
1049 # --------------------------------------------------------------
1050 sub fetch_circ_xacts {
1054 my $start_date = shift;
1055 my $end_date = shift;
1059 # at the specified org and each descendent org,
1060 # fetch the open circs for this user
1061 $U->walk_org_tree( $org,
1064 $logger->debug("collect: searching for open circs at " . $n->shortname);
1067 $e->search_action_circulation(
1081 my $active_ids = fetch_active($e, \@circs, $start_date, $end_date);
1083 for my $cid (@$active_ids) {
1085 $e->retrieve_action_circulation(
1091 circ => [ "billings", "payments", "circ_lib", 'target_copy' ]
1102 sub fetch_grocery_xacts {
1106 my $start_date = shift;
1107 my $end_date = shift;
1110 $U->walk_org_tree( $org,
1113 $logger->debug("collect: searching for open grocery xacts at " . $n->shortname);
1116 $e->search_money_grocery(
1119 billing_location => $n->id,
1129 my $active_ids = fetch_active($e, \@xacts, $start_date, $end_date);
1131 for my $id (@$active_ids) {
1133 $e->retrieve_money_grocery(
1139 mg => [ "billings", "payments", "billing_location" ] }
1149 sub fetch_reservation_xacts {
1153 my $start_date = shift;
1154 my $end_date = shift;
1157 $U->walk_org_tree( $org,
1160 $logger->debug("collect: searching for open grocery xacts at " . $n->shortname);
1163 $e->search_booking_reservation(
1166 pickup_lib => $n->id,
1176 my $active_ids = fetch_active($e, \@xacts, $start_date, $end_date);
1178 for my $id (@$active_ids) {
1180 $e->retrieve_booking_reservation(
1186 bresv => [ "billings", "payments", "pickup_lib" ] }
1198 # --------------------------------------------------------------
1199 # Given a list of xact id's, this returns a list of id's that
1200 # had any activity within the given time span
1201 # --------------------------------------------------------------
1203 my( $e, $ids, $start_date, $end_date ) = @_;
1206 # { payment_ts => { between => [ $start, $end ] } } ' ;)
1209 for my $id (@$ids) {
1211 # see if any billings were created in the given time range
1212 my $bills = $e->search_money_billing (
1215 billing_ts => { between => [ $start_date, $end_date ] },
1224 # see if any payments were created in the given range
1225 $payments = $e->search_money_payment (
1228 payment_ts => { between => [ $start_date, $end_date ] },
1235 push( @active, $id ) if @$bills or @$payments;
1242 __PACKAGE__->register_method(
1243 method => 'create_user_note',
1244 api_name => 'open-ils.collections.patron_note.create',
1248 desc => q/ Adds a note to a patron's account /,
1251 desc => 'The authentication token',
1254 { name => 'user_barcode',
1255 desc => q/The patron's barcode/,
1259 desc => q/The title of the note/,
1264 desc => q/The text of the note/,
1271 Returns SUCCESS event on success, error event otherwise.
1279 sub create_user_note {
1280 my( $self, $conn, $auth, $user_barcode, $title, $note_txt ) = @_;
1282 my $e = new_editor(authtoken=>$auth, xact=>1);
1283 return $e->event unless $e->checkauth;
1284 return $e->event unless $e->allowed('UPDATE_USER'); # XXX Makre more specific perm for this
1286 return $e->event unless
1287 my $card = $e->search_actor_card({barcode=>$user_barcode})->[0];
1289 my $note = Fieldmapper::actor::usr_note->new;
1290 $note->usr($card->usr);
1291 $note->title($title);
1292 $note->creator($e->requestor->id);
1293 $note->create_date('now');
1295 $note->value($note_txt);
1297 $e->create_actor_usr_note($note) or return $e->event;
1299 return OpenILS::Event->new('SUCCESS');