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'}
228 location => [ map {$_->{id}} @$org_ids ]
237 standing_penalty => 4, # PATRON_EXCEEDS_COLLECTIONS_WARNING
238 org_unit => [ map {$_->{id}} @$org_ids ],
240 {stop_date => undef},
241 {stop_date => {'>' => 'now'}}
244 # We are only interested in users that do not have the
245 # circ.collections.exempt setting applied
246 '+aus' => {value => undef},
247 # and we're only interested in users that are not in the
248 # collections tracker table
249 '+mct' => {id => undef}
253 $query->{where}->{'-and'} = [] if $max_set_date or $min_set_date;
254 push(@{$query->{where}->{'-and'}}, {set_date => {'>' => $max_set_date}}) if $max_set_date;
255 push(@{$query->{where}->{'-and'}}, {set_date => {'<' => $min_set_date}}) if $min_set_date;
257 my $req = $ses->request('open-ils.cstore.json_query', $query);
259 # let the client know we're still here
260 $conn->status( new OpenSRF::DomainObject::oilsContinueStatus );
262 return process_users_of_interest_results(
263 $self, $conn, $e, $req, $start, $min_age, '', $location, $max_age);
269 sub process_users_of_interest_results {
270 my($self, $conn, $e, $req, $starttime, @params) = @_;
273 while( my $resp = $req->recv(timeout => 7200) ) {
275 return $req->failed if $req->failed;
276 my $hash = $resp->content;
280 $total = time - $starttime;
281 $logger->info("collections: request (@params) took $total seconds");
284 my $u = $e->retrieve_actor_user(
289 flesh_fields => {au => ["groups","profile", "card"]},
292 ) or return $e->event;
297 profile => $u->profile->name,
298 barcode => $u->card->barcode,
299 groups => [ map { $_->name } @{$u->groups} ],
302 $conn->respond($hash);
309 __PACKAGE__->register_method(
310 method => 'users_owing_money',
311 api_name => 'open-ils.collections.users_owing_money.retrieve',
317 Returns an array of users that owe money during
318 the given time frame at the location (or child locations)
323 desc => 'The authentication token',
326 { name => 'start_date',
327 desc => 'The start of the time interval to check',
328 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
331 { name => 'end_date',
332 desc => q/Then end date of the time interval to check/,
333 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
335 { name => 'fine_level',
336 desc => q/The fine threshold at which users will be included in the search results /,
339 { name => 'locations',
340 desc => q/ A list of one or more org-unit short names.
341 If a selected location has 'child' locations (e.g. a library region), the
342 child locations will be included in the search/,
347 desc => q/An array of user information objects/,
354 sub users_owing_money {
355 my( $self, $conn, $auth, $start_date, $end_date, $fine_level, @locations ) = @_;
357 return OpenILS::Event->new('BAD_PARAMS')
358 unless ($auth and $start_date and $end_date and @locations);
360 my $e = new_editor(authtoken => $auth);
361 return $e->event unless $e->checkauth;
363 # they need global perms to view users so no org is provided
364 return $e->event unless $e->allowed('VIEW_USER');
368 my $ses = OpenSRF::AppSession->create('open-ils.storage');
371 my $req = $ses->request(
372 'open-ils.storage.money.collections.users_owing_money',
373 $start_date, $end_date, $fine_level, @locations);
375 # let the client know we're still here
376 $conn->status( new OpenSRF::DomainObject::oilsContinueStatus );
378 return process_users_of_interest_results(
379 $self, $conn, $e, $req, $start, $start_date, $end_date, $fine_level, @locations);
384 __PACKAGE__->register_method(
385 method => 'users_with_activity',
386 api_name => 'open-ils.collections.users_with_activity.retrieve',
392 Returns an array of users that are already in collections
393 and had any type of billing or payment activity within
394 the given time frame at the location (or child locations)
399 desc => 'The authentication token',
402 { name => 'start_date',
403 desc => 'The start of the time interval to check',
404 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
407 { name => 'end_date',
408 desc => q/Then end date of the time interval to check/,
409 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
411 { name => 'location',
412 desc => q/The short-name of the orginization unit (library) at which the activity occurred.
413 If a selected location has 'child' locations (e.g. a library region), the
414 child locations will be included in the search/,
420 desc => q/An array of user information objects/,
426 sub users_with_activity {
427 my( $self, $conn, $auth, $start_date, $end_date, $location ) = @_;
428 return OpenILS::Event->new('BAD_PARAMS')
429 unless ($auth and $start_date and $end_date and $location);
431 my $e = new_editor(authtoken => $auth);
432 return $e->event unless $e->checkauth;
434 my $org = $e->search_actor_org_unit({shortname => $location})
435 or return $e->event; $org = $org->[0];
436 return $e->event unless $e->allowed('VIEW_USER', $org->id);
438 my $ses = OpenSRF::AppSession->create('open-ils.storage');
441 my $req = $ses->request(
442 'open-ils.storage.money.collections.users_with_activity.atomic',
443 $start_date, $end_date, $location);
445 $conn->status( new OpenSRF::DomainObject::oilsContinueStatus );
448 while( my $resp = $req->recv(timeout => 7200) ) {
451 $total = time - $start;
452 $logger->info("collections: users_with_activity search ".
453 "($start_date, $end_date, $location) took $total seconds");
456 return $req->failed if $req->failed;
457 $conn->respond($resp->content);
465 __PACKAGE__->register_method(
466 method => 'put_into_collections',
467 api_name => 'open-ils.collections.put_into_collections',
472 Marks a user as being "in collections" at a given location
477 desc => 'The authentication token',
481 desc => 'The id of the user to plact into collections',
485 { name => 'location',
486 desc => q/The short-name of the orginization unit (library)
487 for which the user is being placed in collections/,
490 { name => 'fee_amount',
492 The amount of money that a patron should be fined.
493 If this field is empty, no fine is created.
497 { name => 'fee_note',
499 Custom note that is added to the the billing.
500 This field is not required.
501 Note: fee_note is not the billing_type. Billing_type type is
502 decided by the system. (e.g. "fee for collections").
503 fee_note is purely used for any additional needed information
504 and is only visible to staff.
511 desc => q/A SUCCESS event on success, error event on failure/,
516 sub put_into_collections {
517 my( $self, $conn, $auth, $user_id, $location, $fee_amount, $fee_note ) = @_;
519 return OpenILS::Event->new('BAD_PARAMS')
520 unless ($auth and $user_id and $location);
522 my $e = new_editor(authtoken => $auth, xact =>1);
523 return $e->event unless $e->checkauth;
525 my $org = $e->search_actor_org_unit({shortname => $location});
526 return $e->event unless $org = $org->[0];
527 return $e->event unless $e->allowed('money.collections_tracker.create', $org->id);
529 my $existing = $e->search_money_collections_tracker(
531 location => $org->id,
533 collector => $e->requestor->id
538 return OpenILS::Event->new('MONEY_COLLECTIONS_TRACKER_EXISTS') if @$existing;
540 $logger->info("collect: user ".$e->requestor->id.
541 " putting user $user_id into collections for $location");
543 my $tracker = Fieldmapper::money::collections_tracker->new;
545 $tracker->usr($user_id);
546 $tracker->collector($e->requestor->id);
547 $tracker->location($org->id);
548 $tracker->enter_time('now');
550 $e->create_money_collections_tracker($tracker)
554 my $evt = add_collections_fee($e, $user_id, $org, $fee_amount, $fee_note );
560 my $pen = Fieldmapper::actor::user_standing_penalty->new;
561 $pen->org_unit($org->id);
563 $pen->standing_penalty(30); # PATRON_IN_COLLECTIONS
564 $pen->staff($e->requestor->id);
565 $pen->note($fee_note) if $fee_note;
566 $U->simplereq('open-ils.actor', 'open-ils.actor.user.penalty.apply', $auth, $pen);
568 return OpenILS::Event->new('SUCCESS');
571 sub add_collections_fee {
572 my( $e, $patron_id, $org, $fee_amount, $fee_note ) = @_;
576 $logger->info("collect: adding fee to user $patron_id : $fee_amount : $fee_note");
578 my $xact = Fieldmapper::money::grocery->new;
579 $xact->usr($patron_id);
580 $xact->xact_start('now');
581 $xact->billing_location($org->id);
583 $xact = $e->create_money_grocery($xact) or return $e->event;
585 my $bill = Fieldmapper::money::billing->new;
586 $bill->note($fee_note);
587 $bill->xact($xact->id);
589 $bill->billing_type(OILS_BILLING_TYPE_COLLECTION_FEE);
590 $bill->amount($fee_amount);
592 $e->create_money_billing($bill) or return $e->event;
599 __PACKAGE__->register_method(
600 method => 'remove_from_collections',
601 api_name => 'open-ils.collections.remove_from_collections',
603 Returns the users that are currently in collections and
604 had activity during the provided interval. Dates are inclusive.
605 @param start_date The beginning of the activity interval
606 @param end_date The end of the activity interval
607 @param location The location at which the fines were created
612 __PACKAGE__->register_method(
613 method => 'remove_from_collections',
614 api_name => 'open-ils.collections.remove_from_collections',
619 Removes a user from the collections table for the given location
624 desc => 'The authentication token',
628 desc => 'The id of the user to plact into collections',
632 { name => 'location',
633 desc => q/The short-name of the orginization unit (library)
634 for which the user is being removed from collections/,
640 desc => q/A SUCCESS event on success, error event on failure/,
646 sub remove_from_collections {
647 my( $self, $conn, $auth, $user_id, $location ) = @_;
649 return OpenILS::Event->new('BAD_PARAMS')
650 unless ($auth and $user_id and $location);
652 my $e = new_editor(authtoken => $auth, xact=>1);
653 return $e->event unless $e->checkauth;
655 my $org = $e->search_actor_org_unit({shortname => $location})
656 or return $e->event; $org = $org->[0];
657 return $e->event unless $e->allowed('money.collections_tracker.delete', $org->id);
659 my $tracker = $e->search_money_collections_tracker(
660 { usr => $user_id, location => $org->id })
663 $e->delete_money_collections_tracker($tracker->[0])
667 return OpenILS::Event->new('SUCCESS');
671 #__PACKAGE__->register_method(
672 # method => 'transaction_details',
673 # api_name => 'open-ils.collections.user_transaction_details.retrieve',
679 __PACKAGE__->register_method(
680 method => 'transaction_details',
681 api_name => 'open-ils.collections.user_transaction_details.retrieve',
686 Returns a list of fleshed user objects with transaction details
691 desc => 'The authentication token',
694 { name => 'start_date',
695 desc => 'The start of the time interval to check',
696 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
699 { name => 'end_date',
700 desc => q/Then end date of the time interval to check/,
701 type => q/string (ISO 8601 timestamp. E.g. 2006-06-24, 1994-11-05T08:15:30-05:00 /,
703 { name => 'location',
704 desc => q/The short-name of the orginization unit (library) at which the activity occurred.
705 If a selected location has 'child' locations (e.g. a library region), the
706 child locations will be included in the search/,
711 desc => 'An array of user ids',
717 desc => q/A list of objects. Object keys include:
719 transactions : An object with keys :
720 circulations : Fleshed circulation objects
721 grocery : Fleshed 'grocery' transaction objects
728 sub transaction_details {
729 my( $self, $conn, $auth, $start_date, $end_date, $location, $user_list ) = @_;
731 return OpenILS::Event->new('BAD_PARAMS')
732 unless ($auth and $start_date and $end_date and $location and $user_list);
734 my $e = new_editor(authtoken => $auth);
735 return $e->event unless $e->checkauth;
737 # they need global perms to view users so no org is provided
738 return $e->event unless $e->allowed('VIEW_USER');
740 my $org = $e->search_actor_org_unit({shortname => $location})
741 or return $e->event; $org = $org->[0];
743 # get a reference to the org inside of the tree
744 $org = $U->find_org($U->get_org_tree(), $org->id);
747 for my $uid (@$user_list) {
750 $blob->{usr} = $e->retrieve_actor_user(
759 "standing_penalties",
770 $blob->{transactions} = {
772 fetch_circ_xacts($e, $uid, $org, $start_date, $end_date),
774 fetch_grocery_xacts($e, $uid, $org, $start_date, $end_date),
776 fetch_reservation_xacts($e, $uid, $org, $start_date, $end_date)
779 # for each transaction, flesh the workstatoin on any attached payment
780 # and make the payment object a real object (e.g. cash payment),
781 # not just a generic payment object
783 @{$blob->{transactions}->{circulations}},
784 @{$blob->{transactions}->{reservations}},
785 @{$blob->{transactions}->{grocery}} ) {
788 if( $ps = $xact->payments and @$ps ) {
789 my @fleshed; my $evt;
791 ($p, $evt) = flesh_payment($e,$p);
795 $xact->payments(\@fleshed);
799 push( @data, $blob );
805 __PACKAGE__->register_method(
806 method => 'user_balance_summary',
807 api_name => 'open-ils.collections.user_balance_summary.generate',
812 desc => q/Collect balance information for users in collections. By default,
813 only the total balance owed is calculated. Use the "include_xacts"
814 param to include per-transaction summaries as well./,
817 desc => 'The authentication token',
821 Hash of API arguments. Options include:
822 location -- org unit shortname
823 start_date -- ISO 8601 date. limit to patrons added to collections on or after this date (optional).
824 end_date -- ISO 8601 date. limit to patrons added to collections on or before this date (optional).
825 user_id -- retrieve information only for this user (takes preference over
826 start and end_date). May be a single ID or list of IDs. (optional).
827 include_xacts -- If true, include a summary object per transaction in addition to the full balance owed
834 The file name prefix of the file to be created.
835 The file name format will be:
836 user_balance_YYYY-MM-DD_${location}_${start_date}_${end_date}_${user_id}.[tmp|xml]
837 Optional params not provided by the caller will not be part of the file name.
839 user_balance_BR1_2012-05-25_2012-01-01_2012-12-31 # start and end dates
840 user_balance_BR2_2012-05-25_153244 # user id only.
841 In-process files will have a .tmp suffix
842 Completed files will have a .xml suffix
849 sub user_balance_summary {
850 my ($self, $client, $auth, $args) = @_;
852 my $location = $$args{location};
853 my $start_date = $$args{start_date};
854 my $end_date = $$args{end_date};
855 my $user_id = $$args{user_id};
857 return OpenILS::Event->new('BAD_PARAMS')
858 unless $auth and $location and
859 ($start_date or $end_date or $user_id);
861 my $e = new_editor(authtoken => $auth);
862 return $e->event unless $e->checkauth;
864 my $org = $e->search_actor_org_unit({shortname => $location})->[0]
867 # they need global perms to view users so no org is provided
868 return $e->event unless $e->allowed('VIEW_USER', $org->id);
870 my $org_list = $U->get_org_descendants($org->id);
872 my ($evt, $file_prefix, $file_name, $FILE) = setup_batch_file('user_balance', $args);
874 $client->respond_complete($evt || $file_prefix);
879 @user_list = (ref $user_id eq 'ARRAY') ? @$user_id : ($user_id);
882 # collect the users from the tracker table based on the provided filters
885 select => {mct => ['usr']},
887 where => {location => $org_list}
890 $query->{where}->{enter_time} = {'>=' => $start_date};
891 $query->{where}->{enter_time} = {'<=' => $end_date};
892 my $users = $e->json_query($query);
893 @user_list = map {$_->{usr}} @$users;
896 print $FILE "<Collections>\n"; # append to the document as we have data
898 for my $user_id (@user_list) {
899 my $user_doc = XML::LibXML::Document->new;
900 my $root = $user_doc->createElement('User');
901 $user_doc->setDocumentElement($root);
903 my $user = $e->retrieve_actor_user([
910 'standing_penalties',
919 my $au_doc = $user->toXML({no_virt => 1, skip_fields => {au => ['passwd']}});
920 my $au_node = $au_doc->documentElement;
921 $user_doc->adoptNode($au_node);
922 $root->appendChild($au_node);
924 my $circ_ids = $e->search_action_circulation(
925 {usr => $user_id, circ_lib => $org_list, xact_finish => undef},
929 my $groc_ids = $e->search_money_grocery(
930 {usr => $user_id, billing_location => $org_list, xact_finish => undef},
934 my $res_ids = $e->search_booking_reservation(
935 {usr => $user_id, pickup_lib => $org_list, xact_finish => undef},
939 # get the sum owed an all transactions
940 my $balance = $e->json_query({
942 { column => 'balance_owed',
948 where => {id => [@$circ_ids, @$groc_ids, @$res_ids]}
951 $balance = $balance ? $balance->{balance_owed} : '0';
953 my $xacts_node = $user_doc->createElement('Transactions');
954 my $balance_node = $user_doc->createElement('BalanceOwed');
955 $balance_node->appendChild($user_doc->createTextNode($balance));
956 $xacts_node->appendChild($balance_node);
957 $root->appendChild($xacts_node);
959 if ($$args{include_xacts}) {
960 my $xacts = $e->search_money_billable_transaction_summary(
961 {id => [@$circ_ids, @$groc_ids, @$res_ids]},
965 for my $xact (@$xacts) {
966 my $xact_node = $xact->toXML({no_virt => 1})->documentElement;
967 $user_doc->adoptNode($xact_node);
968 $xacts_node->appendChild($xact_node);
972 print $FILE $user_doc->documentElement->toString(1) . "\n";
975 print $FILE "\n</Collections>";
978 (my $complete_file = $file_name) =~ s|.tmp$|.xml|og;
980 unless (move($file_name, $complete_file)) {
981 $logger->error("collections: unable to move ".
982 "user_balance file $file_name => $complete_file : $@");
988 sub setup_batch_file {
991 my $location = $$args{location};
992 my $start_date = $$args{start_date};
993 my $end_date = $$args{end_date};
994 my $user_id = $$args{user_id};
996 my $conf = OpenSRF::Utils::SettingsClient->new;
997 my $dir_name = $conf->config_value(apps =>
998 'open-ils.collections' => app_settings => 'batch_file_dir');
1001 $logger->error("collections: no batch_file_dir directory configured");
1002 return OpenILS::Event->new('COLLECTIONS_FILE_ERROR');
1005 unless (-e $dir_name) {
1006 eval { mkpath($dir_name); };
1008 $logger->error("collections: unable to create batch_file_dir directory $dir_name : $@");
1009 return OpenILS::Event->new('COLLECTIONS_FILE_ERROR');
1013 my $file_prefix = "${prefix}_" . DateTime->now->strftime('%F') . "_$location";
1014 $file_prefix .= "_$start_date" if $start_date;
1015 $file_prefix .= "_$end_date" if $end_date;
1016 $file_prefix .= "_$user_id" if $user_id;
1019 my $file_name = File::Spec->catfile($dir_name, "$file_prefix.tmp");
1021 unless (open($FILE, '>', $file_name)) {
1022 $logger->error("collections: unable to open user_balance_summary file $file_name : $@");
1023 return OpenILS::Event->new('COLLECTIONS_FILE_ERROR');
1026 return (undef, $file_prefix, $file_name, $FILE);
1032 my $type = $p->payment_type;
1033 $logger->debug("collect: fleshing workstation on payment $type : ".$p->id);
1034 my $meth = "retrieve_money_$type";
1035 $p = $e->$meth($p->id) or return (undef, $e->event);
1037 $p->payment_type($type);
1039 $e->retrieve_actor_workstation(
1044 flesh_fields => { aws => [ 'owning_lib' ] }
1049 } catch Error with {};
1054 # --------------------------------------------------------------
1055 # Collect all open circs for the user
1056 # For each circ, see if any billings or payments were created
1057 # during the given time period.
1058 # --------------------------------------------------------------
1059 sub fetch_circ_xacts {
1063 my $start_date = shift;
1064 my $end_date = shift;
1068 # at the specified org and each descendent org,
1069 # fetch the open circs for this user
1070 $U->walk_org_tree( $org,
1073 $logger->debug("collect: searching for open circs at " . $n->shortname);
1076 $e->search_action_circulation(
1090 my $active_ids = fetch_active($e, \@circs, $start_date, $end_date);
1092 for my $cid (@$active_ids) {
1094 $e->retrieve_action_circulation(
1100 circ => [ "billings", "payments", "circ_lib", 'target_copy' ]
1111 sub fetch_grocery_xacts {
1115 my $start_date = shift;
1116 my $end_date = shift;
1119 $U->walk_org_tree( $org,
1122 $logger->debug("collect: searching for open grocery xacts at " . $n->shortname);
1125 $e->search_money_grocery(
1128 billing_location => $n->id,
1138 my $active_ids = fetch_active($e, \@xacts, $start_date, $end_date);
1140 for my $id (@$active_ids) {
1142 $e->retrieve_money_grocery(
1148 mg => [ "billings", "payments", "billing_location" ] }
1158 sub fetch_reservation_xacts {
1162 my $start_date = shift;
1163 my $end_date = shift;
1166 $U->walk_org_tree( $org,
1169 $logger->debug("collect: searching for open grocery xacts at " . $n->shortname);
1172 $e->search_booking_reservation(
1175 pickup_lib => $n->id,
1185 my $active_ids = fetch_active($e, \@xacts, $start_date, $end_date);
1187 for my $id (@$active_ids) {
1189 $e->retrieve_booking_reservation(
1195 bresv => [ "billings", "payments", "pickup_lib" ] }
1207 # --------------------------------------------------------------
1208 # Given a list of xact id's, this returns a list of id's that
1209 # had any activity within the given time span
1210 # --------------------------------------------------------------
1212 my( $e, $ids, $start_date, $end_date ) = @_;
1215 # { payment_ts => { between => [ $start, $end ] } } ' ;)
1218 for my $id (@$ids) {
1220 # see if any billings were created in the given time range
1221 my $bills = $e->search_money_billing (
1224 billing_ts => { between => [ $start_date, $end_date ] },
1233 # see if any payments were created in the given range
1234 $payments = $e->search_money_payment (
1237 payment_ts => { between => [ $start_date, $end_date ] },
1244 push( @active, $id ) if @$bills or @$payments;
1251 __PACKAGE__->register_method(
1252 method => 'create_user_note',
1253 api_name => 'open-ils.collections.patron_note.create',
1257 desc => q/ Adds a note to a patron's account /,
1260 desc => 'The authentication token',
1263 { name => 'user_barcode',
1264 desc => q/The patron's barcode/,
1268 desc => q/The title of the note/,
1273 desc => q/The text of the note/,
1280 Returns SUCCESS event on success, error event otherwise.
1288 sub create_user_note {
1289 my( $self, $conn, $auth, $user_barcode, $title, $note_txt ) = @_;
1291 my $e = new_editor(authtoken=>$auth, xact=>1);
1292 return $e->event unless $e->checkauth;
1293 return $e->event unless $e->allowed('UPDATE_USER'); # XXX Makre more specific perm for this
1295 return $e->event unless
1296 my $card = $e->search_actor_card({barcode=>$user_barcode})->[0];
1298 my $note = Fieldmapper::actor::usr_note->new;
1299 $note->usr($card->usr);
1300 $note->title($title);
1301 $note->creator($e->requestor->id);
1302 $note->create_date('now');
1304 $note->value($note_txt);
1306 $e->create_actor_usr_note($note) or return $e->event;
1308 return OpenILS::Event->new('SUCCESS');