]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ.pm
subclassing OpenSRF::Application so we have a place to put global OpenILS methods
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Circ.pm
1 package OpenILS::Application::Circ;
2 use OpenILS::Application;
3 use base qw/OpenILS::Application/;
4 use strict; use warnings;
5
6 use OpenILS::Application::Circ::Circulate;
7 use OpenILS::Application::Circ::Survey;
8 use OpenILS::Application::Circ::StatCat;
9 use OpenILS::Application::Circ::Holds;
10 use OpenILS::Application::Circ::HoldNotify;
11 use OpenILS::Application::Circ::Money;
12 use OpenILS::Application::Circ::NonCat;
13 use OpenILS::Application::Circ::CopyLocations;
14
15 use DateTime;
16 use DateTime::Format::ISO8601;
17
18 use OpenILS::Application::AppUtils;
19
20 use OpenSRF::Utils qw/:datetime/;
21 use OpenILS::Utils::ModsParser;
22 use OpenILS::Event;
23 use OpenSRF::EX qw(:try);
24 use OpenSRF::Utils::Logger qw(:logger);
25 use OpenILS::Utils::Fieldmapper;
26 use OpenILS::Utils::Editor;
27 use OpenILS::Utils::CStoreEditor q/:funcs/;
28 use OpenILS::Const qw/:const/;
29 use OpenSRF::Utils::SettingsClient;
30
31 my $apputils = "OpenILS::Application::AppUtils";
32 my $U = $apputils;
33
34
35 # ------------------------------------------------------------------------
36 # Top level Circ package;
37 # ------------------------------------------------------------------------
38
39 sub initialize {
40         my $self = shift;
41         OpenILS::Application::Circ::Circulate->initialize();
42 }
43
44
45 __PACKAGE__->register_method(
46         method => 'retrieve_circ',
47         api_name        => 'open-ils.circ.retrieve',
48         signature => q/
49                 Retrieve a circ object by id
50                 @param authtoken Login session key
51                 @pararm circid The id of the circ object
52         /
53 );
54 sub retrieve_circ {
55         my( $s, $c, $a, $i ) = @_;
56         my $e = new_editor(authtoken => $a);
57         return $e->event unless $e->checkauth;
58         my $circ = $e->retrieve_action_circulation($i) or return $e->event;
59         if( $e->requestor->id ne $circ->usr ) {
60                 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
61         }
62         return $circ;
63 }
64
65
66 __PACKAGE__->register_method(
67         method => 'fetch_circ_mods',
68         api_name => 'open-ils.circ.circ_modifier.retrieve.all');
69 sub fetch_circ_mods {
70         my $conf = OpenSRF::Utils::SettingsClient->new;
71         return $conf->config_value(
72                 'apps', 'open-ils.circ', 'app_settings', 'circ_modifiers', 'mod' );
73 }
74
75 __PACKAGE__->register_method(
76         method => 'fetch_bill_types',
77         api_name => 'open-ils.circ.billing_type.retrieve.all');
78 sub fetch_bill_types {
79         my $conf = OpenSRF::Utils::SettingsClient->new;
80         return $conf->config_value(
81                 'apps', 'open-ils.circ', 'app_settings', 'billing_types', 'type' );
82 }
83
84
85 # ------------------------------------------------------------------------
86 # Returns an array of {circ, record} hashes checked out by the user.
87 # ------------------------------------------------------------------------
88 __PACKAGE__->register_method(
89         method  => "checkouts_by_user",
90         api_name        => "open-ils.circ.actor.user.checked_out",
91         NOTES           => <<"  NOTES");
92         Returns a list of open circulations as a pile of objects.  each object
93         contains the relevant copy, circ, and record
94         NOTES
95
96 sub checkouts_by_user {
97         my( $self, $client, $user_session, $user_id ) = @_;
98
99         my( $requestor, $target, $copy, $record, $evt );
100
101         ( $requestor, $target, $evt ) = 
102                 $apputils->checkses_requestor( $user_session, $user_id, 'VIEW_CIRCULATIONS');
103         return $evt if $evt;
104
105         my $circs = $apputils->simplereq(
106                 'open-ils.cstore',
107                 "open-ils.cstore.direct.action.open_circulation.search.atomic", 
108                 { usr => $target->id, checkin_time => undef } );
109 #               { usr => $target->id } );
110
111         my @results;
112         for my $circ (@$circs) {
113
114                 ( $copy, $evt )  = $apputils->fetch_copy($circ->target_copy);
115                 return $evt if $evt;
116
117                 $logger->debug("Retrieving record for copy " . $circ->target_copy);
118
119                 ($record, $evt) = $apputils->fetch_record_by_copy( $circ->target_copy );
120                 return $evt if $evt;
121
122                 my $mods = $apputils->record_to_mvr($record);
123
124                 push( @results, { copy => $copy, circ => $circ, record => $mods } );
125         }
126
127         return \@results;
128
129 }
130
131
132
133 __PACKAGE__->register_method(
134         method  => "checkouts_by_user_slim",
135         api_name        => "open-ils.circ.actor.user.checked_out.slim",
136         NOTES           => <<"  NOTES");
137         Returns a list of open circulation objects
138         NOTES
139
140 # DEPRECAT ME?? XXX
141 sub checkouts_by_user_slim {
142         my( $self, $client, $user_session, $user_id ) = @_;
143
144         my( $requestor, $target, $copy, $record, $evt );
145
146         ( $requestor, $target, $evt ) = 
147                 $apputils->checkses_requestor( $user_session, $user_id, 'VIEW_CIRCULATIONS');
148         return $evt if $evt;
149
150         $logger->debug( 'User ' . $requestor->id . 
151                 " retrieving checked out items for user " . $target->id );
152
153         # XXX Make the call correct..
154         return $apputils->simplereq(
155                 'open-ils.cstore',
156                 "open-ils.cstore.direct.action.open_circulation.search.atomic", 
157                 { usr => $target->id, checkin_time => undef } );
158 #               { usr => $target->id } );
159 }
160
161
162 __PACKAGE__->register_method(
163         method  => "checkouts_by_user_opac",
164         api_name        => "open-ils.circ.actor.user.checked_out.opac",);
165
166 # XXX Deprecate Me
167 sub checkouts_by_user_opac {
168         my( $self, $client, $auth, $user_id ) = @_;
169
170         my $e = OpenILS::Utils::Editor->new( authtoken => $auth );
171         return $e->event unless $e->checkauth;
172         $user_id ||= $e->requestor->id;
173         return $e->event unless 
174                 my $patron = $e->retrieve_actor_user($user_id);
175
176         my $data;
177         my $search = {usr => $user_id, stop_fines => undef};
178
179         if( $user_id ne $e->requestor->id ) {
180                 $data = $e->search_action_circulation(
181                         $search, {checkperm=>1, permorg=>$patron->home_ou})
182                         or return $e->event;
183
184         } else {
185                 $data = $e->search_action_circulation($search);
186         }
187
188         return $data;
189 }
190
191
192 __PACKAGE__->register_method(
193         method  => "title_from_transaction",
194         api_name        => "open-ils.circ.circ_transaction.find_title",
195         NOTES           => <<"  NOTES");
196         Returns a mods object for the title that is linked to from the 
197         copy from the hold that created the given transaction
198         NOTES
199
200 sub title_from_transaction {
201         my( $self, $client, $login_session, $transactionid ) = @_;
202
203         my( $user, $circ, $title, $evt );
204
205         ( $user, $evt ) = $apputils->checkses( $login_session );
206         return $evt if $evt;
207
208         ( $circ, $evt ) = $apputils->fetch_circulation($transactionid);
209         return $evt if $evt;
210         
211         ($title, $evt) = $apputils->fetch_record_by_copy($circ->target_copy);
212         return $evt if $evt;
213
214         return $apputils->record_to_mvr($title);
215 }
216
217
218
219 __PACKAGE__->register_method(
220         method  => "new_set_circ_lost",
221         api_name        => "open-ils.circ.circulation.set_lost",
222         signature       => q/
223         Sets the copy and related open circulation to lost
224                 @param auth
225                 @param args : barcode
226         /
227 );
228
229
230 # ---------------------------------------------------------------------
231 # Sets a circulation to lost.  updates copy status to lost
232 # applies copy and/or prcoessing fees depending on org settings
233 # ---------------------------------------------------------------------
234 sub new_set_circ_lost {
235     my( $self, $conn, $auth, $args ) = @_;
236
237     my $e = new_editor(authtoken=>$auth, xact=>1);
238     return $e->die_event unless $e->checkauth;
239
240     my $barcode = $$args{barcode};
241     $logger->info("marking item lost $barcode");
242
243     # ---------------------------------------------------------------------
244     # gather the pieces
245     my $copy = $e->search_asset_copy([
246         {barcode=>$barcode, deleted=>'f'},
247         {flesh => 1, flesh_fields => {'acp' => ['call_number']}}])->[0] 
248             or return $e->die_event;
249
250     my $owning_lib = 
251         ($copy->call_number == OILS_PRECAT_CALL_NUMBER) ? 
252             $copy->circ_lib : $copy->call_number->owning_lib;
253
254     my $circ = $e->search_action_circulation(
255         {checkin_time => undef, target_copy => $copy->id} )->[0]
256             or return $e->die_event;
257
258     $e->allowed('SET_CIRC_LOST', $circ->circ_lib) or return $e->die_event;
259
260     # ---------------------------------------------------------------------
261     # fetch the related org settings
262     my $default_price = $U->ou_ancestor_setting_value(
263         $owning_lib, OILS_SETTING_DEF_ITEM_PRICE, $e) || 0;
264     my $proc_fee = $U->ou_ancestor_setting_value(
265         $owning_lib, OILS_SETTING_LOST_PROCESSING_FEE, $e) || 0;
266     my $charge_on_0 = $U->ou_ancestor_setting_value(
267         $owning_lib, OILS_SETTING_CHARGE_LOST_ON_ZERO, $e) || 0;
268     my $void_overdue = $U->ou_ancestor_setting_value(
269         $owning_lib, OILS_SETTING_VOID_OVERDUE_ON_LOST, $e) || 0;
270
271     $logger->info("org settings: default price = $default_price, ".
272         "processing fee = $proc_fee, charge on 0 = $charge_on_0, void overdues = $void_overdue");
273
274     # ---------------------------------------------------------------------
275     # move the copy into LOST status
276         unless( $copy->status == OILS_COPY_STATUS_LOST ) {
277                 $copy->status(OILS_COPY_STATUS_LOST);
278         $copy->editor($e->requestor->id);
279         $copy->edit_date('now');
280         $e->update_asset_copy($copy) or return $e->die_event;
281         }
282
283     # ---------------------------------------------------------------------
284     # determine the appropriate item price to charge and create the billing
285     my $price = $copy->price;
286     $price = $default_price unless defined $price;
287     $price = 0 if $price < 0;
288     $price = $default_price if $price == 0 and $charge_on_0;
289
290     if( $price > 0 ) {
291         my $evt = create_bill($e, $price, 'Lost Materials', $circ->id);
292         return $evt if $evt;
293     }
294
295     # ---------------------------------------------------------------------
296     # if there is a processing fee, charge that too
297     if( $proc_fee > 0 ) {
298         my $evt = create_bill($e, $proc_fee, 'Lost Materials Processing Fee', $circ->id);
299         return $evt if $evt;
300     }
301
302     # ---------------------------------------------------------------------
303     # mark the circ as lost and stop the fines
304     $circ->stop_fines(OILS_STOP_FINES_LOST);
305     $circ->stop_fines_time('now') unless $circ->stop_fines_time;
306     $e->update_action_circulation($circ) or return $e->die_event;
307
308     # ---------------------------------------------------------------------
309     # void all overdue fines on this circ if configured
310     if( $void_overdue ) {
311         my $evt = void_overdues($e, $circ);
312         return $evt if $evt;
313     }
314
315     my $evt = reopen_xact($e, $circ->id);
316     return $evt if $evt;
317
318     $e->commit;
319     return 1;
320 }
321
322 sub reopen_xact {
323     my($e, $xactid) = @_;
324
325     # -----------------------------------------------------------------
326     # make sure the transaction is not closed
327     my $xact = $e->retrieve_money_billable_transaction($xactid)
328         or return $e->die_event;
329
330     if( $xact->xact_finish ) {
331         my ($mbts) = $U->fetch_mbts($xactid, $e);
332         if( $mbts->balance_owed != 0 ) {
333             $logger->info("* re-opening xact $xactid, orig xact_finish is ".$xact->xact_finish);
334             $xact->clear_xact_finish;
335             $e->update_money_billable_transaction($xact)
336                 or return $e->die_event;
337         } 
338     }
339 }
340
341
342 sub create_bill {
343         my( $e, $amount, $type, $xactid ) = @_;
344
345         $logger->info("The system is charging $amount [$type] on xact $xactid");
346
347     # -----------------------------------------------------------------
348     # now create the billing
349         my $bill = Fieldmapper::money::billing->new;
350         $bill->xact($xactid);
351         $bill->amount($amount);
352         $bill->billing_type($type); 
353         $bill->note('SYSTEM GENERATED');
354     $e->create_money_billing($bill) or return $e->die_event;
355
356         return undef;
357 }
358
359
360
361 # -----------------------------------------------------------------
362 # Voids overdue fines on the given circ.  if a backdate is 
363 # provided, then we only void back to the backdate
364 # -----------------------------------------------------------------
365 sub void_overdues {
366     my( $e, $circ, $backdate ) = @_;
367
368     my $bill_search = { 
369         xact => $circ->id, 
370         billing_type => OILS_BILLING_TYPE_OVERDUE_MATERIALS 
371     };
372
373     if( $backdate ) {
374         # ------------------------------------------------------------------
375         # Fines for overdue materials are assessed up to, but not including,
376         # one fine interval after the fines are applicable.  Here, we add
377         # one fine interval to the backdate to ensure that we are not 
378         # voiding fines that were applicable before the backdate.
379         # ------------------------------------------------------------------
380         my $interval = OpenSRF::Utils->interval_to_seconds($circ->fine_interval);
381         my $date = DateTime::Format::ISO8601->parse_datetime($backdate);
382         $backdate = $U->epoch2ISO8601($date->epoch + $interval);
383         $logger->info("applying backdate $backdate in overdue voiding");
384         $$bill_search{billing_ts} = {'>=' => $backdate};
385     }
386
387     my $bills = $e->search_money_billing($bill_search);
388     
389     for my $bill (@$bills) {
390         next if $U->is_true($bill->voided);
391         $logger->info("voiding overdue bill ".$bill->id);
392         $bill->voided('t');
393         $bill->void_time('now');
394         $bill->voider($e->requestor->id);
395         my $n = $bill->note || "";
396         $bill->note("$n\nSystem: VOIDED FOR BACKDATE");
397         $e->update_money_billing($bill) or return $e->die_event;
398     }
399
400         return undef;
401 }
402
403
404
405
406 __PACKAGE__->register_method(
407         method  => "set_circ_claims_returned",
408         api_name        => "open-ils.circ.circulation.set_claims_returned",
409         signature       => q/
410         Sets the circ for the given item as claims returned
411         If a backdate is provided, overdue fines will be voided
412         back to the backdate
413                 @param auth
414                 @param args : barcode, backdate
415         /
416 );
417
418 sub set_circ_claims_returned {
419     my( $self, $conn, $auth, $args ) = @_;
420
421     my $e = new_editor(authtoken=>$auth, xact=>1);
422     return $e->die_event unless $e->checkauth;
423
424     my $barcode = $$args{barcode};
425     my $backdate = $$args{backdate};
426
427     $logger->info("marking circ for item $barcode as claims returned".
428         (($backdate) ? " with backdate $backdate" : ''));
429
430     my $copy = $e->search_asset_copy({barcode=>$barcode, deleted=>'f'})->[0] 
431         or return $e->die_event;
432
433     my $circ = $e->search_action_circulation(
434         {checkin_time => undef, target_copy => $copy->id})->[0]
435             or return $e->die_event;
436
437     $e->allowed('SET_CIRC_CLAIMS_RETURNED', $circ->circ_lib) 
438         or return $e->die_event;
439
440     $circ->stop_fines(OILS_STOP_FINES_CLAIMSRETURNED);
441         $circ->stop_fines_time('now') unless $circ->stop_fines_time;
442
443     if( $backdate ) {
444         # make it look like the circ stopped at the cliams returned time
445         $circ->stop_fines_time(clense_ISO8601($backdate));
446         my $evt = void_overdues($e, $circ, $backdate);
447         return $evt if $evt;
448     }
449
450     $e->update_action_circulation($circ) or return $e->die_event;
451     $e->commit;
452     return 1;
453 }
454
455
456
457
458
459 __PACKAGE__->register_method (
460         method          => 'set_circ_due_date',
461         api_name                => 'open-ils.circ.circulation.due_date.update',
462         signature       => q/
463                 Updates the due_date on the given circ
464                 @param authtoken
465                 @param circid The id of the circ to update
466                 @param date The timestamp of the new due date
467         /
468 );
469
470 sub set_circ_due_date {
471         my( $self, $conn, $auth, $circ_id, $date ) = @_;
472
473     my $e = new_editor(xact=>1, authtoken=>$auth);
474     return $e->die_event unless $e->checkauth;
475     my $circ = $e->retrieve_action_circulation($circ_id)
476         or return $e->die_event;
477
478     return $e->die_event unless $e->allowed('CIRC_OVERRIDE_DUE_DATE', $circ->circ_lib);
479         $date = clense_ISO8601($date);
480         $circ->due_date($date);
481     $e->update_action_circulation($circ) or return $e->die_event;
482     $e->commit;
483
484     return $circ->id;
485 }
486
487
488 __PACKAGE__->register_method(
489         method          => "create_in_house_use",
490         api_name                => 'open-ils.circ.in_house_use.create',
491         signature       =>      q/
492                 Creates an in-house use action.
493                 @param $authtoken The login session key
494                 @param params A hash of params including
495                         'location' The org unit id where the in-house use occurs
496                         'copyid' The copy in question
497                         'count' The number of in-house uses to apply to this copy
498                 @return An array of id's representing the id's of the newly created
499                 in-house use objects or an event on an error
500         /);
501
502 __PACKAGE__->register_method(
503         method          => "create_in_house_use",
504         api_name                => 'open-ils.circ.non_cat_in_house_use.create',
505 );
506
507 =head OLD CODE
508 sub ___create_in_house_use {
509         my( $self, $client, $authtoken, $params ) = @_;
510
511         my( $staff, $evt, $copy );
512         my $org                 = $params->{location};
513         my $copyid              = $params->{copyid};
514         my $count               = $params->{count} || 1;
515         my $nc_type             = $params->{non_cat_type};
516         my $use_time    = $params->{use_time} || 'now';
517
518         my $non_cat = 1 if $self->api_name =~ /non_cat/;
519
520         unless( $non_cat ) {
521                 unless( $copyid ) {
522                         my $barcode = $params->{barcode};
523                         ($copy, $evt) = $U->fetch_copy_by_barcode($barcode);
524                         return $evt if $evt;
525                         $copyid = $copy->id;
526                 }
527                 ($copy, $evt) = $U->fetch_copy($copyid) unless $copy;
528                 return $evt if $evt;
529         }
530
531         ($staff, $evt) = $U->checkses($authtoken);
532         return $evt if $evt;
533
534         $evt = $U->check_perms($staff->id, $org, 'CREATE_IN_HOUSE_USE');
535         return $evt if $evt;
536
537         if( $use_time ne 'now' ) {
538                 $use_time = clense_ISO8601($use_time);
539                 $logger->debug("in_house_use setting use time to $use_time");
540         }
541
542         my @ids;
543         for(1..$count) {
544
545                 my $ihu;
546                 my $method;
547
548                 if($non_cat) {
549                         $ihu = Fieldmapper::action::non_cat_in_house_use->new;
550                         $ihu->noncat_type($nc_type);
551                         $method = 'open-ils.storage.direct.action.non_cat_in_house_use.create';
552                 } else {
553                         $ihu = Fieldmapper::action::in_house_use->new;
554                         $ihu->item($copyid);
555                         $method = 'open-ils.storage.direct.action.in_house_use.create';
556                 }
557
558                 $ihu->staff($staff->id);
559                 $ihu->org_unit($org);
560                 $ihu->use_time($use_time);
561
562                 my $id = $U->simplereq('open-ils.storage', $method, $ihu );
563
564                 return $U->DB_UPDATE_FAILED($ihu) unless $id;
565                 push @ids, $id;
566         }
567
568         return \@ids;
569 }
570 =cut
571
572
573
574
575 sub create_in_house_use {
576         my( $self, $client, $auth, $params ) = @_;
577
578         my( $evt, $copy );
579         my $org                 = $params->{location};
580         my $copyid              = $params->{copyid};
581         my $count               = $params->{count} || 1;
582         my $nc_type             = $params->{non_cat_type};
583         my $use_time    = $params->{use_time} || 'now';
584
585         my $e = new_editor(xact=>1,authtoken=>$auth);
586         return $e->event unless $e->checkauth;
587         return $e->event unless $e->allowed('CREATE_IN_HOUSE_USE');
588
589         my $non_cat = 1 if $self->api_name =~ /non_cat/;
590
591         unless( $non_cat ) {
592                 if( $copyid ) {
593                         $copy = $e->retrieve_asset_copy($copyid) or return $e->event;
594                 } else {
595                         $copy = $e->search_asset_copy({barcode=>$params->{barcode}, deleted => 'f'})->[0]
596                                 or return $e->event;
597                         $copyid = $copy->id;
598                 }
599         }
600
601         if( $use_time ne 'now' ) {
602                 $use_time = clense_ISO8601($use_time);
603                 $logger->debug("in_house_use setting use time to $use_time");
604         }
605
606         my @ids;
607         for(1..$count) {
608
609                 my $ihu;
610                 my $method;
611                 my $cmeth;
612
613                 if($non_cat) {
614                         $ihu = Fieldmapper::action::non_cat_in_house_use->new;
615                         $ihu->item_type($nc_type);
616                         $method = 'open-ils.storage.direct.action.non_cat_in_house_use.create';
617                         $cmeth = "create_action_non_cat_in_house_use";
618
619                 } else {
620                         $ihu = Fieldmapper::action::in_house_use->new;
621                         $ihu->item($copyid);
622                         $method = 'open-ils.storage.direct.action.in_house_use.create';
623                         $cmeth = "create_action_in_house_use";
624                 }
625
626                 $ihu->staff($e->requestor->id);
627                 $ihu->org_unit($org);
628                 $ihu->use_time($use_time);
629
630                 $ihu = $e->$cmeth($ihu) or return $e->event;
631                 push( @ids, $ihu->id );
632         }
633
634         $e->commit;
635         return \@ids;
636 }
637
638
639
640
641
642 __PACKAGE__->register_method(
643         method  => "view_circs",
644         api_name        => "open-ils.circ.copy_checkout_history.retrieve",
645         notes           => q/
646                 Retrieves the last X circs for a given copy
647                 @param authtoken The login session key
648                 @param copyid The copy to check
649                 @param count How far to go back in the item history
650                 @return An array of circ ids
651         /);
652
653
654
655 sub view_circs {
656         my( $self, $client, $authtoken, $copyid, $count ) = @_; 
657
658         my( $requestor, $evt ) = $U->checksesperm(
659                         $authtoken, 'VIEW_COPY_CHECKOUT_HISTORY' );
660         return $evt if $evt;
661
662         return [] unless $count;
663
664         my $circs = $U->cstorereq(
665                 'open-ils.cstore.direct.action.circulation.search.atomic',
666                         { 
667                                 target_copy => $copyid, 
668                         }, 
669                         { 
670                                 limit => $count, 
671                                 order_by => { circ => "xact_start DESC" }
672                         } 
673         );
674
675         return $circs;
676 }
677
678
679 __PACKAGE__->register_method(
680         method  => "circ_count",
681         api_name        => "open-ils.circ.circulation.count",
682         notes           => q/
683                 Returns the number of times the item has circulated
684                 @param copyid The copy to check
685         /);
686
687 sub circ_count {
688         my( $self, $client, $copyid, $range ) = @_; 
689         my $e = OpenILS::Utils::Editor->new;
690         return $e->request('open-ils.storage.asset.copy.circ_count', $copyid, $range);
691 }
692
693
694
695 __PACKAGE__->register_method(
696         method          => 'fetch_notes',
697         api_name                => 'open-ils.circ.copy_note.retrieve.all',
698         signature       => q/
699                 Returns an array of copy note objects.  
700                 @param args A named hash of parameters including:
701                         authtoken       : Required if viewing non-public notes
702                         itemid          : The id of the item whose notes we want to retrieve
703                         pub                     : True if all the caller wants are public notes
704                 @return An array of note objects
705         /);
706
707 __PACKAGE__->register_method(
708         method          => 'fetch_notes',
709         api_name                => 'open-ils.circ.call_number_note.retrieve.all',
710         signature       => q/@see open-ils.circ.copy_note.retrieve.all/);
711
712 __PACKAGE__->register_method(
713         method          => 'fetch_notes',
714         api_name                => 'open-ils.circ.title_note.retrieve.all',
715         signature       => q/@see open-ils.circ.copy_note.retrieve.all/);
716
717
718 # NOTE: VIEW_COPY/VOLUME/TITLE_NOTES perms should always be global
719 sub fetch_notes {
720         my( $self, $connection, $args ) = @_;
721
722         my $id = $$args{itemid};
723         my $authtoken = $$args{authtoken};
724         my( $r, $evt);
725
726         if( $self->api_name =~ /copy/ ) {
727                 if( $$args{pub} ) {
728                         return $U->cstorereq(
729                                 'open-ils.cstore.direct.asset.copy_note.search.atomic',
730                                 { owning_copy => $id, pub => 't' } );
731                 } else {
732                         ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_COPY_NOTES');
733                         return $evt if $evt;
734                         return $U->cstorereq(
735                                 'open-ils.cstore.direct.asset.copy_note.search.atomic', {owning_copy => $id} );
736                 }
737
738         } elsif( $self->api_name =~ /call_number/ ) {
739                 if( $$args{pub} ) {
740                         return $U->cstorereq(
741                                 'open-ils.cstore.direct.asset.call_number_note.search.atomic',
742                                 { call_number => $id, pub => 't' } );
743                 } else {
744                         ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_VOLUME_NOTES');
745                         return $evt if $evt;
746                         return $U->cstorereq(
747                                 'open-ils.cstore.direct.asset.call_number_note.search.atomic', { call_number => $id } );
748                 }
749
750         } elsif( $self->api_name =~ /title/ ) {
751                 if( $$args{pub} ) {
752                         return $U->cstorereq(
753                                 'open-ils.cstore.direct.bilbio.record_note.search.atomic',
754                                 { record => $id, pub => 't' } );
755                 } else {
756                         ( $r, $evt ) = $U->checksesperm($authtoken, 'VIEW_TITLE_NOTES');
757                         return $evt if $evt;
758                         return $U->cstorereq(
759                                 'open-ils.cstore.direct.biblio.record_note.search.atomic', { record => $id } );
760                 }
761         }
762
763         return undef;
764 }
765
766 __PACKAGE__->register_method(
767         method  => 'has_notes',
768         api_name        => 'open-ils.circ.copy.has_notes');
769 __PACKAGE__->register_method(
770         method  => 'has_notes',
771         api_name        => 'open-ils.circ.call_number.has_notes');
772 __PACKAGE__->register_method(
773         method  => 'has_notes',
774         api_name        => 'open-ils.circ.title.has_notes');
775
776
777 sub has_notes {
778         my( $self, $conn, $authtoken, $id ) = @_;
779         my $editor = OpenILS::Utils::Editor->new(authtoken => $authtoken);
780         return $editor->event unless $editor->checkauth;
781
782         my $n = $editor->search_asset_copy_note(
783                 {owning_copy=>$id}, {idlist=>1}) if $self->api_name =~ /copy/;
784
785         $n = $editor->search_asset_call_number_note(
786                 {call_number=>$id}, {idlist=>1}) if $self->api_name =~ /call_number/;
787
788         $n = $editor->search_biblio_record_note(
789                 {record=>$id}, {idlist=>1}) if $self->api_name =~ /title/;
790
791         return scalar @$n;
792 }
793
794
795
796 __PACKAGE__->register_method(
797         method          => 'create_copy_note',
798         api_name                => 'open-ils.circ.copy_note.create',
799         signature       => q/
800                 Creates a new copy note
801                 @param authtoken The login session key
802                 @param note     The note object to create
803                 @return The id of the new note object
804         /);
805
806 sub create_copy_note {
807         my( $self, $connection, $authtoken, $note ) = @_;
808
809         my $e = new_editor(xact=>1, authtoken=>$authtoken);
810         return $e->event unless $e->checkauth;
811         my $copy = $e->retrieve_asset_copy(
812                 [
813                         $note->owning_copy,
814                         {       flesh => 1,
815                                 flesh_fields => { 'acp' => ['call_number'] }
816                         }
817                 ]
818         );
819
820         return $e->event unless 
821                 $e->allowed('CREATE_COPY_NOTE', $copy->call_number->owning_lib);
822
823         $note->create_date('now');
824         $note->creator($e->requestor->id);
825         $note->pub( ($U->is_true($note->pub)) ? 't' : 'f' );
826         $note->clear_id;
827
828         $e->create_asset_copy_note($note) or return $e->event;
829         $e->commit;
830         return $note->id;
831 }
832
833
834 __PACKAGE__->register_method(
835         method          => 'delete_copy_note',
836         api_name                =>      'open-ils.circ.copy_note.delete',
837         signature       => q/
838                 Deletes an existing copy note
839                 @param authtoken The login session key
840                 @param noteid The id of the note to delete
841                 @return 1 on success - Event otherwise.
842                 /);
843 sub delete_copy_note {
844         my( $self, $conn, $authtoken, $noteid ) = @_;
845
846         my $e = new_editor(xact=>1, authtoken=>$authtoken);
847         return $e->die_event unless $e->checkauth;
848
849         my $note = $e->retrieve_asset_copy_note([
850                 $noteid,
851                 { flesh => 2,
852                         flesh_fields => {
853                                 'acpn' => [ 'owning_copy' ],
854                                 'acp' => [ 'call_number' ],
855                         }
856                 }
857         ]) or return $e->die_event;
858
859         if( $note->creator ne $e->requestor->id ) {
860                 return $e->die_event unless 
861                         $e->allowed('DELETE_COPY_NOTE', $note->owning_copy->call_number->owning_lib);
862         }
863
864         $e->delete_asset_copy_note($note) or return $e->die_event;
865         $e->commit;
866         return 1;
867 }
868
869
870 __PACKAGE__->register_method(
871         method => 'age_hold_rules',
872         api_name        =>  'open-ils.circ.config.rules.age_hold_protect.retrieve.all',
873 );
874
875 sub age_hold_rules {
876         my( $self, $conn ) = @_;
877         return new_editor()->retrieve_all_config_rules_age_hold_protect();
878 }
879
880
881
882 __PACKAGE__->register_method(
883         method => 'copy_details_barcode',
884         api_name => 'open-ils.circ.copy_details.retrieve.barcode');
885 sub copy_details_barcode {
886         my( $self, $conn, $auth, $barcode ) = @_;
887     my $e = new_editor();
888     my $cid = $e->search_asset_copy({barcode=>$barcode, deleted=>'f'}, {idlist=>1})->[0];
889     return $e->event unless $cid;
890         return $self->copy_details( $conn, $auth, $cid );
891 }
892
893
894 __PACKAGE__->register_method(
895         method => 'copy_details',
896         api_name => 'open-ils.circ.copy_details.retrieve');
897
898 sub copy_details {
899         my( $self, $conn, $auth, $copy_id ) = @_;
900         my $e = new_editor(authtoken=>$auth);
901         return $e->event unless $e->checkauth;
902
903         my $flesh = { flesh => 1 };
904
905         my $copy = $e->retrieve_asset_copy(
906                 [
907                         $copy_id,
908                         {
909                                 flesh => 2,
910                                 flesh_fields => {
911                                         acp => ['call_number'],
912                                         acn => ['record']
913                                 }
914                         }
915                 ]) or return $e->event;
916
917
918         # De-flesh the copy for backwards compatibility
919         my $mvr;
920         my $vol = $copy->call_number;
921         if( ref $vol ) {
922                 $copy->call_number($vol->id);
923                 my $record = $vol->record;
924                 if( ref $record ) {
925                         $vol->record($record->id);
926                         $mvr = $U->record_to_mvr($record);
927                 }
928         }
929
930
931         my $hold = $e->search_action_hold_request(
932                 { 
933                         current_copy            => $copy_id, 
934                         capture_time            => { "!=" => undef },
935                         fulfillment_time        => undef,
936                         cancel_time                     => undef,
937                 }
938         )->[0];
939
940         OpenILS::Application::Circ::Holds::flesh_hold_transits([$hold]) if $hold;
941
942         my $transit = $e->search_action_transit_copy(
943                 { target_copy => $copy_id, dest_recv_time => undef } )->[0];
944
945         # find the latest circ, open or closed
946         my $circ = $e->search_action_circulation(
947                 [
948                         { target_copy => $copy_id },
949                         { order_by => { circ => 'xact_start desc' }, limit => 1 }
950                 ]
951         )->[0];
952
953
954         return {
955                 copy            => $copy,
956                 hold            => $hold,
957                 transit => $transit,
958                 circ            => $circ,
959                 volume  => $vol,
960                 mvr             => $mvr,
961         };
962 }
963
964
965
966
967 __PACKAGE__->register_method(
968         method => 'mark_item',
969         api_name => 'open-ils.circ.mark_item_damaged',
970 );
971 __PACKAGE__->register_method(
972         method => 'mark_item',
973         api_name => 'open-ils.circ.mark_item_missing',
974 );
975
976 sub mark_item {
977         my( $self, $conn, $auth, $copy_id ) = @_;
978         my $e = new_editor(authtoken=>$auth, xact =>1);
979         return $e->event unless $e->checkauth;
980
981         my $perm = 'MARK_ITEM_MISSING';
982         my $stat = OILS_COPY_STATUS_MISSING;
983
984         if( $self->api_name =~ /damaged/ ) {
985                 $perm = 'MARK_ITEM_DAMAGED';
986                 $stat = OILS_COPY_STATUS_DAMAGED;
987         }
988
989         my $copy = $e->retrieve_asset_copy($copy_id)
990                 or return $e->event;
991         $copy->status($stat);
992         $copy->edit_date('now');
993         $copy->editor($e->requestor->id);
994
995         $e->update_asset_copy($copy) or return $e->event;
996
997
998         my $holds = $e->search_action_hold_request(
999                 { 
1000                         current_copy => $copy->id,
1001                         fulfillment_time => undef,
1002                         cancel_time => undef,
1003                 }
1004         );
1005
1006         $e->commit;
1007
1008         $logger->debug("reseting holds that target the marked copy");
1009         OpenILS::Application::Circ::Holds->_reset_hold($e->requestor, $_) for @$holds;
1010
1011         return 1;
1012 }
1013
1014
1015
1016
1017
1018
1019 # ----------------------------------------------------------------------
1020 __PACKAGE__->register_method(
1021         method => 'magic_fetch',
1022         api_name => 'open-ils.agent.fetch'
1023 );
1024
1025 my @FETCH_ALLOWED = qw/ aou aout acp acn bre /;
1026
1027 sub magic_fetch {
1028         my( $self, $conn, $auth, $args ) = @_;
1029         my $e = new_editor( authtoken => $auth );
1030         return $e->event unless $e->checkauth;
1031
1032         my $hint = $$args{hint};
1033         my $id  = $$args{id};
1034
1035         # Is the call allowed to fetch this type of object?
1036         return undef unless grep { $_ eq $hint } @FETCH_ALLOWED;
1037
1038         # Find the class the iplements the given hint
1039         my ($class) = grep { 
1040                 $Fieldmapper::fieldmap->{$_}{hint} eq $hint } Fieldmapper->classes;
1041
1042         $class =~ s/Fieldmapper:://og;
1043         $class =~ s/::/_/og;
1044         my $method = "retrieve_$class";
1045
1046         my $obj = $e->$method($id) or return $e->event;
1047         return $obj;
1048 }
1049 # ----------------------------------------------------------------------
1050
1051
1052 __PACKAGE__->register_method(
1053         method  => "fleshed_circ_retrieve",
1054         api_name        => "open-ils.circ.fleshed.retrieve",);
1055
1056 sub fleshed_circ_retrieve {
1057         my( $self, $client, $id ) = @_;
1058         my $e = new_editor();
1059         my $circ = $e->retrieve_action_circulation(
1060                 [
1061                         $id,
1062                         { 
1063                                 flesh                           => 4,
1064                                 flesh_fields    => { 
1065                                         circ => [ qw/ target_copy / ],
1066                                         acp => [ qw/ location status stat_cat_entry_copy_maps notes age_protect call_number / ],
1067                                         ascecm => [ qw/ stat_cat stat_cat_entry / ],
1068                                         acn => [ qw/ record / ],
1069                                 }
1070                         }
1071                 ]
1072         ) or return $e->event;
1073         
1074         my $copy = $circ->target_copy;
1075         my $vol = $copy->call_number;
1076         my $rec = $circ->target_copy->call_number->record;
1077
1078         $vol->record($rec->id);
1079         $copy->call_number($vol->id);
1080         $circ->target_copy($copy->id);
1081
1082         my $mvr;
1083
1084         if( $rec->id == OILS_PRECAT_RECORD ) {
1085                 $rec = undef;
1086                 $vol = undef;
1087         } else { 
1088                 $mvr = $U->record_to_mvr($rec);
1089                 $rec->marc(''); # drop the bulky marc data
1090         }
1091
1092         return {
1093                 circ => $circ,
1094                 copy => $copy,
1095                 volume => $vol,
1096                 record => $rec,
1097                 mvr => $mvr,
1098         };
1099 }
1100
1101
1102
1103
1104 1;