]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Circ/Rules.pm
f36d97d4a44e4460806c2dc753c75682b32b2e4d
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Circ / Rules.pm
1 # ---------------------------------------------------------------
2 # Copyright (C) 2005  Georgia Public Library Service 
3 # Bill Erickson <highfalutin@gmail.com>
4
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 # ---------------------------------------------------------------
15
16 package OpenILS::Application::Circ::Rules;
17 use base qw/OpenSRF::Application/;
18 use strict; use warnings;
19
20 use OpenSRF::Utils::SettingsClient;
21 use OpenILS::Utils::Fieldmapper;
22 use OpenILS::EX;
23 use OpenSRF::Utils::Logger qw(:level); 
24
25 use Template qw(:template);
26 use Template::Stash; 
27
28 use Time::HiRes qw(time);
29 use OpenILS::Utils::ModsParser;
30
31 use OpenSRF::Utils;
32 use OpenSRF::EX qw(:try);
33
34 use OpenILS::Application::AppUtils;
35 my $apputils = "OpenILS::Application::AppUtils";
36 use Digest::MD5 qw(md5_hex);
37
38 my $log = "OpenSRF::Utils::Logger";
39
40 # ----------------------------------------------------------------
41 # rules scripts
42 my $circ_script;
43 my $permission_script;
44 my $duration_script;
45 my $recurring_fines_script;
46 my $max_fines_script;
47 my $permit_hold_script;
48 my $permit_renew_script;
49 # ----------------------------------------------------------------
50
51
52 # data used for this circulation transaction
53 my $circ_objects = {};
54
55 # some static data from the database
56 my $copy_statuses;
57 my $patron_standings;
58 my $patron_profiles;
59 my $shelving_locations;
60
61 # template stash
62 my $stash;
63
64 # memcache for caching the circ objects
65 my $cache_handle;
66
67
68 use constant NO_COPY => 100;
69
70 sub initialize {
71
72         my $self = shift;
73         my $conf = OpenSRF::Utils::SettingsClient->new;
74
75         # ----------------------------------------------------------------
76         # set up the rules scripts
77         # ----------------------------------------------------------------
78         $circ_script = $conf->config_value(                                     
79                 "apps", "open-ils.circ","app_settings", "rules", "main");
80
81         $permission_script = $conf->config_value(                       
82                 "apps", "open-ils.circ","app_settings", "rules", "permission");
83
84         $duration_script = $conf->config_value(                 
85                 "apps", "open-ils.circ","app_settings", "rules", "duration");
86
87         $recurring_fines_script = $conf->config_value(  
88                 "apps", "open-ils.circ","app_settings", "rules", "recurring_fines");
89
90         $max_fines_script = $conf->config_value(                        
91                 "apps", "open-ils.circ","app_settings", "rules", "max_fines");
92
93         $permit_hold_script = $conf->config_value(
94                 "apps", "open-ils.circ","app_settings", "rules", "permit_hold");
95
96         $permit_renew_script = $conf->config_value(
97                 "apps", "open-ils.circ","app_settings", "rules", "permit_renew");
98
99         $log->debug("Loaded rules scripts for circ:\n".
100                 "main - $circ_script : permit circ - $permission_script\n".
101                 "duration - $duration_script : recurring - $recurring_fines_script\n".
102                 "max fines - $max_fines_script : permit hold - $permit_hold_script", DEBUG);
103
104
105         $cache_handle = OpenSRF::Utils::Cache->new();
106 }
107
108
109 sub _grab_patron_standings {
110         my $session = shift;
111         if(!$patron_standings) {
112                 my $standing_req = $session->request(
113                         "open-ils.storage.direct.config.standing.retrieve.all.atomic");
114                 $patron_standings = $standing_req->gather(1);
115                 $patron_standings =
116                         { map { (''.$_->id => $_->value) } @$patron_standings };
117         }
118 }
119
120 sub _grab_patron_profiles {
121         my $session = shift;
122         if(!$patron_profiles) {
123                 my $profile_req = $session->request(
124                         "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
125                 $patron_profiles = $profile_req->gather(1);
126                 $patron_profiles =
127                         { map { (''.$_->id => $_->name) } @$patron_profiles };
128         }
129
130 }
131
132 sub _grab_user {
133         my $session = shift;
134         my $patron_id = shift;
135         my $patron_req  = $session->request(
136                 "open-ils.storage.direct.actor.user.retrieve", 
137                 $patron_id );
138         return $patron_req->gather(1);
139 }
140         
141
142 sub _grab_title_by_copy {
143         my $session = shift;
144         my $copyid = shift;
145         my $title_req   = $session->request(
146                 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
147                 $copyid );
148         return $title_req->gather(1);
149 }
150
151 sub _grab_patron_summary {
152         my $session = shift;
153         my $patron_id = shift;
154         my $summary_req = $session->request(
155                 "open-ils.storage.action.circulation.patron_summary",
156                 $patron_id );
157         return $summary_req->gather(1);
158 }
159
160 sub _grab_copy_by_barcode {
161         my($session, $barcode) = @_;
162         warn "Searching for copy with barcode $barcode\n";
163         my $copy_req    = $session->request(
164                 "open-ils.storage.fleshed.asset.copy.search.barcode", 
165                 $barcode );
166         return $copy_req->gather(1);
167 }
168
169 sub _grab_copy_by_id {
170         my($session, $id) = @_;
171         warn "Searching for copy with id $id\n";
172         my $copy_req    = $session->request(
173                 "open-ils.storage.direct.asset.copy.retrieve", 
174                 $id );
175         my $c = $copy_req->gather(1);
176         if($c) { return _grab_copy_by_barcode($session, $c->barcode); }
177         return undef;
178 }
179
180
181 sub gather_hold_objects {
182         my($session, $hold, $copy, $args) = @_;
183
184         _grab_patron_standings($session);
185         _grab_patron_profiles($session);
186
187
188         # flesh me
189         $copy = _grab_copy_by_barcode($session, $copy->barcode);
190
191         my $hold_objects = {};
192         $hold_objects->{standings} = $patron_standings;
193         $hold_objects->{copy}           = $copy;
194         $hold_objects->{hold}           = $hold;
195         $hold_objects->{title}          = $$args{title} || _grab_title_by_copy($session, $copy->id);
196         $hold_objects->{requestor} = _grab_user($session, $hold->requestor);
197         my $patron                                              = _grab_user($session, $hold->usr);
198
199         $copy->status( $copy->status->name );
200         $patron->standing($patron_standings->{$patron->standing()});
201         $patron->profile( $patron_profiles->{$patron->profile});
202
203         $hold_objects->{patron}         = $patron;
204
205         return $hold_objects;
206 }
207
208
209
210 __PACKAGE__->register_method(
211         method  => "permit_hold",
212         api_name        => "open-ils.circ.permit_hold",
213         notes           => <<NOTES
214 Determines whether a given copy is eligible to be held
215 NOTES
216 );
217
218 sub permit_hold {
219         my( $self, $client, $hold, $copy, $args ) = @_;
220
221         my $session     = OpenSRF::AppSession->create("open-ils.storage");
222         
223         # collect items necessary for circ calculation
224         my $hold_objects = gather_hold_objects( $session, $hold, $copy, $args );
225
226         $stash = Template::Stash->new(
227                         circ_objects                    => $hold_objects,
228                         result                                  => []);
229
230         $stash->set("run_block", $permit_hold_script);
231
232         # grab the number of copies checked out by the patron as
233         # well as the total fines
234         my $summary = _grab_patron_summary($session, $hold_objects->{patron}->id);
235         $summary->[0] ||= 0;
236         $summary->[1] ||= 0.0;
237
238         $stash->set("patron_copies", $summary->[0] );
239         $stash->set("patron_fines", $summary->[1] );
240
241         # run the permissibility script
242         run_script();
243         my $result = $stash->get("result");
244
245         # 0 means OK in the script
246         return 1 if($result->[0] == 0);
247         return 0;
248
249 }
250
251
252
253
254
255 # ----------------------------------------------------------------
256 # Collect all of the objects necessary for calculating the
257 # circ matrix.
258 # ----------------------------------------------------------------
259 sub gather_circ_objects {
260         my( $session, $barcode_string, $patron_id ) = @_;
261
262         throw OpenSRF::EX::ERROR 
263                 ("gather_circ_objects needs data")
264                         unless ($barcode_string and $patron_id);
265
266         warn "Gathering circ objects with barcode $barcode_string and patron id $patron_id\n";
267
268         # see if all of the circ objects are in cache
269         my $cache_key =  "circ_object_" . md5_hex( $barcode_string, $patron_id );
270         $circ_objects = $cache_handle->get_cache($cache_key);
271
272         if($circ_objects) { 
273                 $stash = Template::Stash->new(
274                         circ_objects                    => $circ_objects,
275                         result                                  => [],
276                         target_copy_status      => 1,
277                         );
278                 return;
279         }
280
281         # only necessary if the circ objects have not been built yet
282
283         _grab_patron_standings($session);
284         _grab_patron_profiles($session);
285
286
287         my $copy = _grab_copy_by_barcode($session, $barcode_string);
288         if(!$copy) { return NO_COPY; }
289
290         my $patron = _grab_user($session, $patron_id);
291
292         $copy->status( $copy->status->name );
293         $circ_objects->{copy} = $copy;
294
295         $patron->standing($patron_standings->{$patron->standing()});
296         $patron->profile( $patron_profiles->{$patron->profile});
297         $circ_objects->{patron} = $patron;
298         $circ_objects->{standings} = $patron_standings;
299
300         #$circ_objects->{title} = $title_req->gather(1);
301         $circ_objects->{title} = _grab_title_by_copy($session, $circ_objects->{copy}->id);
302         $cache_handle->put_cache( $cache_key, $circ_objects, 30 );
303
304         $stash = Template::Stash->new(
305                         circ_objects                    => $circ_objects,
306                         result                                  => [],
307                         target_copy_status      => 1,
308                         );
309 }
310
311
312
313 sub run_script {
314
315         my $result;
316
317         my $template = Template->new(
318                 { 
319                         STASH                   => $stash,
320                         ABSOLUTE                => 1, 
321                         OUTPUT          => \$result,
322                 }
323         );
324
325         my $status = $template->process($circ_script);
326
327         if(!$status) { 
328                 throw OpenSRF::EX::ERROR 
329                         ("Error processing circ script " .  $template->error()); 
330         }
331
332         warn "Script result: $result\n";
333 }
334
335
336
337
338 __PACKAGE__->register_method(
339         method  => "permit_circ",
340         api_name        => "open-ils.circ.permit_checkout",
341 );
342
343 sub permit_circ {
344         my( $self, $client, $user_session, $barcode, $user_id, $outstanding_count ) = @_;
345
346         $outstanding_count ||= 0;
347
348         my $session     = OpenSRF::AppSession->create("open-ils.storage");
349         
350         # collect items necessary for circ calculation
351         my $status = gather_circ_objects( $session, $barcode, $user_id );
352
353         if( $status == NO_COPY ) {
354                 return { record => undef, 
355                         status => NO_COPY, 
356                         text => "No copy available with barcode $barcode"
357                 };
358         }
359
360         $stash->set("run_block", $permission_script);
361
362         # grab the number of copies checked out by the patron as
363         # well as the total fines
364         my $summary_req = $session->request(
365                 "open-ils.storage.action.circulation.patron_summary",
366                 $stash->get("circ_objects")->{patron}->id );
367         my $summary = $summary_req->gather(1);
368
369         $summary->[0] ||= 0;
370         $summary->[1] ||= 0.0;
371
372         $stash->set("patron_copies", $summary->[0]  + $outstanding_count );
373         $stash->set("patron_fines", $summary->[1] );
374
375         # run the permissibility script
376         run_script();
377         my $obj = $stash->get("circ_objects");
378
379         # turn the biblio record into a friendly object
380         my $u = OpenILS::Utils::ModsParser->new();
381         $u->start_mods_batch( $obj->{title}->marc() );
382         my $mods = $u->finish_mods_batch();
383
384         my $arr = $stash->get("result");
385         return { record => $mods, status => $arr->[0], text => $arr->[1] };
386
387 }
388
389
390
391 __PACKAGE__->register_method(
392         method  => "circulate",
393         api_name        => "open-ils.circ.checkout.barcode",
394 );
395
396 sub circulate {
397         my( $self, $client, $user_session, $barcode, $patron ) = @_;
398
399
400         my $session = $apputils->start_db_session();
401
402         gather_circ_objects( $session, $barcode, $patron );
403
404         # grab the copy statuses if we don't already have them
405         if(!$copy_statuses) {
406                 my $csreq = $session->request(
407                         "open-ils.storage.direct.config.copy_status.retrieve.all.atomic" );
408                 $copy_statuses = $csreq->gather(1);
409         }
410
411         # put copy statuses into the stash
412         $stash->set("copy_statuses", $copy_statuses );
413
414         my $copy = $circ_objects->{copy};
415         my ($circ, $duration, $recurring, $max) =  run_circ_scripts($session);
416
417         # commit new circ object to db
418         my $commit = $session->request(
419                 "open-ils.storage.direct.action.circulation.create",
420                 $circ );
421         my $id = $commit->gather(1);
422
423         if(!$id) {
424                 throw OpenSRF::EX::ERROR 
425                         ("Error creating new circulation object");
426         }
427
428         # update the copy with the new circ
429         $copy->status( $stash->get("target_copy_status") );
430         $copy->location( $copy->location->id );
431         $copy->circ_lib( $copy->circ_lib->id );
432
433         # commit copy to db
434         my $copy_update = $session->request(
435                 "open-ils.storage.direct.asset.copy.update",
436                 $copy );
437         $copy_update->gather(1);
438
439         $apputils->commit_db_session($session);
440
441         # remove our circ object from the cache
442         $cache_handle->delete_cache("circ_object_" . md5_hex($barcode, $patron));
443
444         # re-retrieve the the committed circ object  
445         $circ = $apputils->simple_scalar_request(
446                 "open-ils.storage",
447                 "open-ils.storage.direct.action.circulation.retrieve",
448                 $id );
449
450
451         # push the rules and due date into the circ object
452         $circ->duration_rule($duration);
453         $circ->max_fine_rule($max);
454         $circ->recuring_fine_rule($recurring);
455
456
457 #       my $due_date = 
458 #               OpenSRF::Utils->interval_to_seconds( 
459 #               $circ->duration ) + int(time());
460
461 #       this comes from an earlier setting now
462 #       $circ->due_date($due_date);
463
464         return $circ;
465
466 }
467
468
469
470 # runs the duration, recurring_fines, and max_fines scripts.
471 # builds the new circ object based on the rules returned from 
472 # these scripts. 
473 # returns (circ, duration_rule, recurring_fines_rule, max_fines_rule)
474 sub run_circ_scripts {
475         my $session = shift;
476
477         # go through all of the scripts and process
478         # each script returns 
479         # [ rule_name, level (appropriate to the script) ]
480         $stash->set("result", [] );
481         $stash->set("run_block", $duration_script);
482         run_script();
483         my $duration_rule = $stash->get("result");
484
485         $stash->set("run_block", $recurring_fines_script);
486         $stash->set("result", [] );
487         run_script();
488         my $rec_fines_rule = $stash->get("result");
489
490         $stash->set("run_block", $max_fines_script);
491         $stash->set("result", [] );
492         run_script();
493         my $max_fines_rule = $stash->get("result");
494
495         my $obj = $stash->get("circ_objects");
496
497         # ----------------------------------------------------------
498         # find the rules objects based on the rule names returned from
499         # the various scripts.
500         my $dur_req = $session->request(
501                 "open-ils.storage.direct.config.rules.circ_duration.search.name.atomic",
502                 $duration_rule->[0] );
503
504         my $rec_req = $session->request(
505                 "open-ils.storage.direct.config.rules.recuring_fine.search.name.atomic",
506                 $rec_fines_rule->[0] );
507
508         my $max_req = $session->request(
509                 "open-ils.storage.direct.config.rules.max_fine.search.name.atomic",
510                 $max_fines_rule->[0] );
511
512         my $duration    = $dur_req->gather(1)->[0];
513         my $recurring   = $rec_req->gather(1)->[0];
514         my $max                 = $max_req->gather(1)->[0];
515
516         my $copy = $circ_objects->{copy};
517
518         use Data::Dumper;
519         warn "Building a new circulation object with\n".
520                 "=> copy "                              . Dumper($copy) .
521                 "=> duration_rule "     . Dumper($duration_rule) .
522                 "=> rec_files_rule " . Dumper($rec_fines_rule) .
523                 "=> duration "                  . Dumper($duration) .
524                 "=> recurring "         . Dumper($recurring) .
525                 "=> max "                               . Dumper($max);
526
527
528         # build the new circ object
529         my $circ =  build_circ_object($session, $copy, $duration_rule->[1], 
530                         $rec_fines_rule->[1], $duration, $recurring, $max );
531
532         return ($circ, $duration, $recurring, $max);
533
534 }
535
536 # ------------------------------------------------------------------
537 # Builds a new circ object
538 # ------------------------------------------------------------------
539 sub build_circ_object {
540         my( $session, $copy, $dur_level, $rec_level, 
541                         $duration, $recurring, $max ) = @_;
542
543         my $circ = new Fieldmapper::action::circulation;
544
545         $circ->circ_lib( $copy->circ_lib->id() );
546         if($dur_level == 1) {
547                 $circ->duration( $duration->shrt );
548         } elsif($dur_level == 2) {
549                 $circ->duration( $duration->normal );
550         } elsif($dur_level == 3) {
551                 $circ->duration( $duration->extended );
552         }
553
554         if($rec_level == 1) {
555                 $circ->recuring_fine( $recurring->low );
556         } elsif($rec_level == 2) {
557                 $circ->recuring_fine( $recurring->normal );
558         } elsif($rec_level == 3) {
559                 $circ->recuring_fine( $recurring->high );
560         }
561
562         $circ->duration_rule( $duration->name );
563         $circ->recuring_fine_rule( $recurring->name );
564         $circ->max_fine_rule( $max->name );
565         $circ->max_fine( $max->amount );
566
567         $circ->fine_interval($recurring->recurance_interval);
568         $circ->renewal_remaining( $duration->max_renewals );
569         $circ->target_copy( $copy->id );
570         $circ->usr( $circ_objects->{patron}->id );
571
572         return $circ;
573
574 }
575
576 __PACKAGE__->register_method(
577         method  => "checkin",
578         api_name        => "open-ils.circ.checkin.barcode",
579 );
580
581 sub checkin {
582         my( $self, $client, $user_session, $barcode ) = @_;
583
584         my $err;
585         my $copy;
586
587         try {
588                 my $session = $apputils->start_db_session();
589         
590                 warn "retrieving copy for checkin\n";
591
592                 if(!$shelving_locations) {
593                         my $sh_req = $session->request(
594                                 "open-ils.storage.direct.asset.copy_location.retrieve.all.atomic");
595                         $shelving_locations = $sh_req->gather(1);
596                         $shelving_locations = 
597                                 { map { (''.$_->id => $_->name) } @$shelving_locations };
598                 }
599         
600                 my $copy_req = $session->request(
601                         "open-ils.storage.direct.asset.copy.search.barcode.atomic", 
602                         $barcode );
603                 $copy = $copy_req->gather(1)->[0];
604                 if(!$copy) {
605                         $client->respond_complete(
606                                         OpenILS::EX->new("UNKNOWN_BARCODE")->ex);
607                 }
608
609                 $copy->status(0);
610         
611                 # find circ's where the transaction is still open for the
612                 # given copy.  should only be one.
613                 warn "Retrieving circ for checking\n";
614                 my $circ_req = $session->request(
615                         "open-ils.storage.direct.action.circulation.search.atomic.atomic",
616                         { target_copy => $copy->id, xact_finish => undef } );
617         
618                 my $circ = $circ_req->gather(1)->[0];
619         
620                 if(!$circ) {
621                         $err = "No circulation exists for the given barcode";
622
623                 } else {
624         
625                         warn "Checking in circ ". $circ->id . "\n";
626                 
627                         $circ->stop_fines("CHECKIN");
628                         $circ->xact_finish("now");
629                 
630                         my $cp_up = $session->request(
631                                 "open-ils.storage.direct.asset.copy.update",
632                                 $copy );
633                         $cp_up->gather(1);
634                 
635                         my $ci_up = $session->request(
636                                 "open-ils.storage.direct.action.circulation.update",
637                                 $circ );
638                         $ci_up->gather(1);
639                 
640                         $apputils->commit_db_session($session);
641                 
642                         warn "Checkin succeeded\n";
643                 }
644         
645         } catch Error with {
646                 my $e = shift;
647                 $err = "Error checking in: $e";
648         };
649         
650         if($err) {
651                 return { record => undef, status => -1, text => $err };
652
653         } else {
654
655                 my $record = $apputils->simple_scalar_request(
656                         "open-ils.storage",
657                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
658                         $copy->id() );
659
660                 my $u = OpenILS::Utils::ModsParser->new();
661                 $u->start_mods_batch( $record->marc() );
662                 my $mods = $u->finish_mods_batch();
663                 return { record => $mods, status => 0, text => "OK", 
664                         route_to => $shelving_locations->{$copy->location} };
665         }
666
667         return 1;
668
669 }
670
671
672
673
674
675
676
677
678 # ------------------------------------------------------------------------------
679 # RENEWALS
680 # ------------------------------------------------------------------------------
681
682
683 __PACKAGE__->register_method(
684         method  => "renew",
685         api_name        => "open-ils.circ.renew",
686         notes           => <<"  NOTES");
687         open-ils.circ.renew(login_session, circ_object);
688         Renews the provided circulation.  login_session is the requestor of the
689         renewal and if the logged in user is not the same as circ->usr, then
690         the logged in user must have RENEW_CIRC permissions.
691         NOTES
692
693 sub renew {
694         my($self, $client, $login_session, $circ) = @_;
695         my $user = $apputils->check_user_session($login_session);
696
697         if($user->id ne $circ->usr) {
698                 if($apputils->check_user_perms($user->id, $user->home_ou, "RENEW_CIRC")) {
699                         return OpenILS::Perm->new("RENEW_CIRC");
700                 }
701         }
702
703         my $session = OpenSRF::AppSession->create("open-ils.storage");
704
705         #XXX XXX See if the copy this circ points to is needed to fulfill a hold!
706
707         my $renew_objects = gather_renew_objects( $session, $circ );
708         if(!ref($renew_objects)) {
709                 if($renew_objects == NO_COPY) {
710                         return { 
711                                 status => NO_COPY, 
712                                 text => "No copy available with id " . $circ->target_copy };
713                 }
714         }
715
716         $stash = Template::Stash->new(
717                         circ_objects                    => $renew_objects,
718                         result                                  => []);
719
720         $stash->set("run_block", $permit_renew_script);
721
722         # grab the number of copies checked out by the patron as
723         # well as the total fines
724         my $summary = _grab_patron_summary($session, $renew_objects->{patron}->id);
725         $summary->[0] ||= 0;
726         $summary->[1] ||= 0.0;
727
728         $stash->set("patron_copies", $summary->[0] );
729         $stash->set("patron_fines", $summary->[1] );
730
731         # run the permissibility script
732         run_script();
733
734         return $stash->get("result");
735
736 }
737
738
739
740 sub gather_renew_objects {
741         my($session, $circ) = @_;
742
743         _grab_patron_standings($session);
744         _grab_patron_profiles($session);
745
746         # flesh me
747         my $copy = _grab_copy_by_id($session, $circ->target_copy);
748         if(!$copy) { return NO_COPY; }
749
750         my $renew_objects = {};
751         $renew_objects->{standings} = $patron_standings;
752         $renew_objects->{copy}          = $copy;
753         $renew_objects->{circ}          = $circ;
754         $renew_objects->{title}         = _grab_title_by_copy($session, $copy->id);
755         my $patron                                              = _grab_user($session, $circ->usr);
756
757         $copy->status( $copy->status->name );
758         $patron->standing($patron_standings->{$patron->standing()});
759         $patron->profile( $patron_profiles->{$patron->profile});
760
761         $renew_objects->{patron}                = $patron;
762
763         return $renew_objects;
764 }
765
766
767
768
769
770 1;