]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Cat/AssetCommon.pm
7f09f2c0171c18a0b7f33c86ea9f6d72382dcd52
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Cat / AssetCommon.pm
1 package OpenILS::Application::Cat::AssetCommon;
2 use strict; use warnings;
3 use OpenILS::Application::Cat::BibCommon;
4 use OpenILS::Utils::CStoreEditor q/:funcs/;
5 use OpenSRF::Utils::Logger qw($logger);
6 use OpenILS::Application::Cat::Merge;
7 use OpenILS::Application::AppUtils;
8 use OpenILS::Utils::Fieldmapper;
9 use OpenILS::Const qw/:const/;
10 use OpenSRF::AppSession;
11 use OpenILS::Event;
12 my $U = 'OpenILS::Application::AppUtils';
13
14
15 # ---------------------------------------------------------------------------
16 # Shared copy mangling code.  Do not publish methods from here.
17 # ---------------------------------------------------------------------------
18
19 sub org_cannot_have_vols {
20     my($class, $e, $org_id) = @_;
21         my $org = $e->retrieve_actor_org_unit([
22         $org_id,
23         {   flesh => 1,
24             flesh_fields => {aou => ['ou_type']}
25         }]) or return $e->event;
26
27         return OpenILS::Event->new('ORG_CANNOT_HAVE_VOLS')
28                 unless $U->is_true($org->ou_type->can_have_vols);
29
30         return 0;
31 }
32
33 sub fix_copy_price {
34     my $class = shift;
35         my $copy = shift;
36
37     if(defined $copy->price) {
38             my $p = $copy->price || 0;
39             $p =~ s/\$//og;
40             $copy->price($p);
41     }
42
43         my $d = $copy->deposit_amount || 0;
44         $d =~ s/\$//og;
45         $copy->deposit_amount($d);
46 }
47
48 sub create_copy {
49         my($class, $editor, $vol, $copy) = @_;
50
51         my $existing = $editor->search_asset_copy(
52                 { barcode => $copy->barcode, deleted => 'f' } );
53         
54         return OpenILS::Event->new('ITEM_BARCODE_EXISTS') if @$existing;
55
56    # see if the volume this copy references is marked as deleted
57     return OpenILS::Event->new('VOLUME_DELETED', vol => $vol->id) 
58         if $U->is_true($vol->deleted);
59
60         my $evt;
61         my $org = (ref $copy->circ_lib) ? $copy->circ_lib->id : $copy->circ_lib;
62         return $evt if ($evt = OpenILS::Application::Cat::AssetCommon->org_cannot_have_vols($editor, $org));
63
64         $copy->clear_id;
65         $copy->editor($editor->requestor->id);
66         $copy->creator($editor->requestor->id);
67         $copy->create_date('now');
68     $copy->call_number($vol->id);
69         $class->fix_copy_price($copy);
70
71         $editor->create_asset_copy($copy) or return $editor->die_event;
72         return undef;
73 }
74
75
76 # if 'delete_stats' is true, the copy->stat_cat_entries data is 
77 # treated as the authoritative list for the copy. existing entries
78 # that are not in said list will be deleted from the DB
79 sub update_copy_stat_entries {
80         my($class, $editor, $copy, $delete_stats) = @_;
81
82         return undef if $copy->isdeleted;
83         return undef unless $copy->ischanged or $copy->isnew;
84
85         my $evt;
86         my $entries = $copy->stat_cat_entries;
87
88         if( $delete_stats ) {
89                 $entries = ($entries and @$entries) ? $entries : [];
90         } else {
91                 return undef unless ($entries and @$entries);
92         }
93
94         my $maps = $editor->search_asset_stat_cat_entry_copy_map({owning_copy=>$copy->id});
95
96         if(!$copy->isnew) {
97                 # if there is no stat cat entry on the copy who's id matches the
98                 # current map's id, remove the map from the database
99                 for my $map (@$maps) {
100                         if(! grep { $_->id == $map->stat_cat_entry } @$entries ) {
101
102                                 $logger->info("copy update found stale ".
103                                         "stat cat entry map ".$map->id. " on copy ".$copy->id);
104
105                                 $editor->delete_asset_stat_cat_entry_copy_map($map)
106                                         or return $editor->event;
107                         }
108                 }
109         }
110
111         # go through the stat cat update/create process
112         for my $entry (@$entries) { 
113                 next unless $entry;
114
115                 # if this link already exists in the DB, don't attempt to re-create it
116                 next if( grep{$_->stat_cat_entry == $entry->id} @$maps );
117         
118                 my $new_map = Fieldmapper::asset::stat_cat_entry_copy_map->new();
119
120                 my $sc = ref($entry->stat_cat) ? $entry->stat_cat->id : $entry->stat_cat;
121                 
122                 $new_map->stat_cat( $sc );
123                 $new_map->stat_cat_entry( $entry->id );
124                 $new_map->owning_copy( $copy->id );
125
126                 $editor->create_asset_stat_cat_entry_copy_map($new_map)
127                         or return $editor->event;
128
129                 $logger->info("copy update created new stat cat entry map ".$editor->data);
130         }
131
132         return undef;
133 }
134
135
136 sub update_copy {
137         my($class, $editor, $override, $vol, $copy) = @_;
138
139         my $evt;
140         my $org = (ref $copy->circ_lib) ? $copy->circ_lib->id : $copy->circ_lib;
141         return $evt if ( $evt = OpenILS::Application::Cat::AssetCommon->org_cannot_have_vols($editor, $org) );
142
143         $logger->info("vol-update: updating copy ".$copy->id);
144         my $orig_copy = $editor->retrieve_asset_copy($copy->id);
145         my $orig_vol  = $editor->retrieve_asset_call_number($copy->call_number);
146
147         $copy->editor($editor->requestor->id);
148         $copy->edit_date('now');
149
150         $copy->age_protect( $copy->age_protect->id )
151                 if ref $copy->age_protect;
152
153         $class->fix_copy_price($copy);
154
155         return $editor->event unless $editor->update_asset_copy($copy);
156         return $class->remove_empty_objects($editor, $override, $orig_vol);
157 }
158
159
160 # this does the actual work
161 sub update_fleshed_copies {
162         my($class, $editor, $override, $vol, $copies, $delete_stats) = @_;
163
164         my $evt;
165         my $fetchvol = ($vol) ? 0 : 1;
166
167         my %cache;
168         $cache{$vol->id} = $vol if $vol;
169
170         for my $copy (@$copies) {
171
172                 my $copyid = $copy->id;
173                 $logger->info("vol-update: inspecting copy $copyid");
174
175                 if( !($vol = $cache{$copy->call_number}) ) {
176                         $vol = $cache{$copy->call_number} = 
177                                 $editor->retrieve_asset_call_number($copy->call_number);
178                         return $editor->event unless $vol;
179                 }
180
181                 return $editor->event unless 
182                         $editor->allowed('UPDATE_COPY', copy_perm_org($vol, $copy));
183
184                 $copy->editor($editor->requestor->id);
185                 $copy->edit_date('now');
186
187                 $copy->status( $copy->status->id ) if ref($copy->status);
188                 $copy->location( $copy->location->id ) if ref($copy->location);
189                 $copy->circ_lib( $copy->circ_lib->id ) if ref($copy->circ_lib);
190                 
191                 my $sc_entries = $copy->stat_cat_entries;
192                 $copy->clear_stat_cat_entries;
193
194                 if( $copy->isdeleted ) {
195                         $evt = $class->delete_copy($editor, $override, $vol, $copy);
196                         return $evt if $evt;
197
198                 } elsif( $copy->isnew ) {
199                         $evt = $class->create_copy( $editor, $vol, $copy );
200                         return $evt if $evt;
201
202                 } elsif( $copy->ischanged ) {
203
204                         $evt = $class->update_copy( $editor, $override, $vol, $copy );
205                         return $evt if $evt;
206                 }
207
208                 $copy->stat_cat_entries( $sc_entries );
209                 $evt = $class->update_copy_stat_entries($editor, $copy, $delete_stats);
210                 return $evt if $evt;
211         }
212
213         $logger->debug("vol-update: done updating copy batch");
214
215         return undef;
216 }
217
218
219 sub delete_copy {
220         my($class, $editor, $override, $vol, $copy ) = @_;
221
222    return $editor->event unless 
223       $editor->allowed('DELETE_COPY',copy_perm_org($vol, $copy));
224
225         my $stat = $U->copy_status($copy->status)->id;
226
227         unless($override) {
228                 return OpenILS::Event->new('COPY_DELETE_WARNING', payload => $copy->id )
229                         if $stat == OILS_COPY_STATUS_CHECKED_OUT or
230                                 $stat == OILS_COPY_STATUS_IN_TRANSIT or
231                                 $stat == OILS_COPY_STATUS_ON_HOLDS_SHELF or
232                                 $stat == OILS_COPY_STATUS_ILL;
233         }
234
235         $logger->info("vol-update: deleting copy ".$copy->id);
236         $copy->deleted('t');
237
238         $copy->editor($editor->requestor->id);
239         $copy->edit_date('now');
240         $editor->update_asset_copy($copy) or return $editor->event;
241
242         # Delete any open transits for this copy
243         my $transits = $editor->search_action_transit_copy(
244                 { target_copy=>$copy->id, dest_recv_time => undef } );
245
246         for my $t (@$transits) {
247                 $editor->delete_action_transit_copy($t)
248                         or return $editor->event;
249         }
250
251         return $class->remove_empty_objects($editor, $override, $vol);
252 }
253
254
255
256 sub create_volume {
257         my($class, $override, $editor, $vol) = @_;
258         my $evt;
259
260         return $evt if ( $evt = $class->org_cannot_have_vols($editor, $vol->owning_lib) );
261
262    # see if the record this volume references is marked as deleted
263    my $rec = $editor->retrieve_biblio_record_entry($vol->record)
264       or return $editor->die_event;
265    return OpenILS::Event->new('BIB_RECORD_DELETED', rec => $rec->id) 
266       if $U->is_true($rec->deleted);
267
268         # first lets see if there are any collisions
269         my $vols = $editor->search_asset_call_number( { 
270                         owning_lib      => $vol->owning_lib,
271                         record          => $vol->record,
272                         label                   => $vol->label,
273                         deleted         => 'f'
274                 }
275         );
276
277         my $label = undef;
278         if(@$vols) {
279       # we've found an exising volume
280                 if($override) { 
281                         $label = $vol->label;
282                 } else {
283                         return OpenILS::Event->new(
284                                 'VOLUME_LABEL_EXISTS', payload => $vol->id);
285                 }
286         }
287
288         # create a temp label so we can create the new volume, 
289     # then de-dup it with the existing volume
290         $vol->label( "__SYSTEM_TMP_$$".time) if $label;
291
292         $vol->creator($editor->requestor->id);
293         $vol->create_date('now');
294         $vol->editor($editor->requestor->id);
295         $vol->edit_date('now');
296         $vol->clear_id;
297
298         $editor->create_asset_call_number($vol) or return $editor->die_event;
299
300         if($label) {
301                 # now restore the label and merge into the existing record
302                 $vol->label($label);
303                 (undef, $evt) = 
304                         OpenILS::Application::Cat::Merge::merge_volumes($editor, [$vol], $$vols[0]);
305                 return $evt if $evt;
306         }
307
308         return undef;
309 }
310
311 # returns the volume if it exists
312 sub volume_exists {
313     my($class, $e, $rec_id, $label, $owning_lib) = @_;
314     return $e->search_asset_call_number(
315         {label => $label, record => $rec_id, owning_lib => $owning_lib, deleted => 'f'})->[0];
316 }
317
318 sub find_or_create_volume {
319         my($class, $e, $label, $record_id, $org_id) = @_;
320
321     my $vol;
322
323     if($record_id == OILS_PRECAT_RECORD) {
324         $vol = $e->retrieve_asset_call_number(OILS_PRECAT_CALL_NUMBER)
325             or return (undef, $e->die_event);
326
327     } else {
328         $vol = $class->volume_exists($e, $record_id, $label, $org_id);
329     }
330
331         # If the volume exists, return the ID
332     return ($vol, undef, 1) if $vol;
333
334         # -----------------------------------------------------------------
335         # Otherwise, create a new volume with the given attributes
336         # -----------------------------------------------------------------
337         return (undef, $e->die_event) unless $e->allowed('UPDATE_VOLUME', $org_id);
338
339         $vol = Fieldmapper::asset::call_number->new;
340         $vol->owning_lib($org_id);
341         $vol->label($label);
342         $vol->record($record_id);
343
344     my $evt = OpenILS::Application::Cat::AssetCommon->create_volume(0, $e, $vol);
345     return (undef, $evt) if $evt;
346
347         return ($vol);
348 }
349
350
351 sub create_copy_note {
352     my($class, $e, $copy, $title, $value, $pub) = @_;
353     my $note = Fieldmapper::asset::copy_note->new;
354     $note->owning_copy($copy->id);
355     $note->creator($e->requestor->id);
356     $note->pub('t');
357     $note->value($value);
358     $note->title($title);
359     $e->create_asset_copy_note($note) or return $e->die_event;
360     return undef;
361 }
362
363
364 sub remove_empty_objects {
365         my($class, $editor, $override, $vol) = @_; 
366
367     my $koe = $U->ou_ancestor_setting_value(
368         $editor->requestor->ws_ou, 'cat.bib.keep_on_empty', $editor);
369     my $aoe =  $U->ou_ancestor_setting_value(
370         $editor->requestor->ws_ou, 'cat.bib.alert_on_empty', $editor);
371
372         if( OpenILS::Application::Cat::BibCommon->title_is_empty($editor, $vol->record, $vol->id) ) {
373
374         # delete this volume if it's not already marked as deleted
375         unless( $U->is_true($vol->deleted) || $vol->isdeleted ) {
376             $vol->deleted('t');
377             $vol->editor($editor->requestor->id);
378             $vol->edit_date('now');
379             $editor->update_asset_call_number($vol) or return $editor->event;
380         }
381
382         unless($koe) {
383             # delete the bib record if the keep-on-empty setting is not set
384             my $evt = OpenILS::Application::Cat::BibCommon->delete_rec($editor, $vol->record);
385             return $evt if $evt;
386         }
387
388         # return the empty alert if the alert-on-empty setting is set
389         return OpenILS::Event->new('TITLE_LAST_COPY', payload => $vol->record ) if $aoe;
390         }
391
392         return undef;
393 }
394
395
396