]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/CDBI.pm
fdf915b127989a9874306db30ca73e7c887d1523
[Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / CDBI.pm
1 package OpenILS::Application::Storage::CDBI;
2 use UNIVERSAL::require; 
3 BEGIN {                 
4         'Class::DBI::Frozen::301'->use or 'Class::DBI'->use or die $@;
5 }     
6 use base qw/Class::DBI/;
7 use Class::DBI::AbstractSearch;
8
9 use OpenILS::Application::Storage::CDBI::actor;
10 use OpenILS::Application::Storage::CDBI::action;
11 use OpenILS::Application::Storage::CDBI::booking;
12 use OpenILS::Application::Storage::CDBI::asset;
13 use OpenILS::Application::Storage::CDBI::authority;
14 use OpenILS::Application::Storage::CDBI::biblio;
15 use OpenILS::Application::Storage::CDBI::config;
16 use OpenILS::Application::Storage::CDBI::metabib;
17 use OpenILS::Application::Storage::CDBI::money;
18 use OpenILS::Application::Storage::CDBI::permission;
19 use OpenILS::Application::Storage::CDBI::container;
20
21 use OpenSRF::Utils::JSON;
22 use OpenSRF::Utils::Logger qw(:level);
23 use OpenSRF::EX qw/:try/;
24
25 our $VERSION = 1;
26 my $log = 'OpenSRF::Utils::Logger';
27
28 if ($Class::DBI::VERSION gt '3.0.1') {
29     $log->error("Your version of Class::DBI, $Class::DBI::VERSION, is too new and incompatible with Evergreen.  You will need to downgrade to version 3.0.1 or install Class::DBI::Frozen::301"); 
30     die("Your version of Class::DBI, $Class::DBI::VERSION, is too new and incompatible with Evergreen.  You will need to downgrade to version 3.0.1 or install Class::DBI::Frozen::301"); 
31 }
32
33 sub child_init {
34         my $self = shift;
35
36         $log->debug("Creating ImaDBI Querys", DEBUG);
37         __PACKAGE__->set_sql( 'OILSFastSearch', <<"     SQL", 'Main');
38                 SELECT  %s
39                   FROM  %s
40                   WHERE %s = ?
41         SQL
42
43         __PACKAGE__->set_sql( 'OILSFastOrderedSearchLike', <<"  SQL", 'Main');
44                 SELECT  %s
45                   FROM  %s
46                   WHERE %s LIKE ?
47                   ORDER BY %s
48         SQL
49
50         __PACKAGE__->set_sql( 'OILSFastOrderedSearch', <<"      SQL", 'Main');
51                 SELECT  %s
52                   FROM  %s
53                   WHERE %s = ?
54                   ORDER BY %s
55         SQL
56
57         $log->debug("Calling Driver child_init", DEBUG);
58         $self->SUPER::child_init(@_);
59
60 }
61
62 sub fast_flesh_sth {
63         my $class = shift;
64         $class = ref($class) || $class;
65
66         my $field = shift;
67         my $value = shift;
68         my $order = shift;
69         my $like = shift;
70
71
72         if (!(defined($order) and ref($order) and ref($order) eq 'HASH')) {
73                 if (defined($value) and ref($value) and ref($value) eq 'HASH') {
74                         $order = $value;
75                         $value = undef;
76                 } else {
77                         $order = { order_by => $class->columns('Primary') }
78                 }
79         }
80
81         unless (defined $value) {
82                 $value = $field;
83                 ($field) = $class->columns('Primary');
84         }
85
86         unless (defined $field) {
87                 ($field) = $class->columns('Primary');
88         }
89
90         unless ($order->{order_by}) {
91                 $order = { order_by => $class->columns('Primary') }
92         }
93
94         my $fm_class = 'Fieldmapper::'.$class;
95         my $field_list = join ',', $class->columns('Essential');
96         
97         my $sth;
98         if (!$like) {
99                 $sth = $class->sql_OILSFastOrderedSearch( $field_list, $class->table, $field, $order->{order_by});
100         } else {
101                 $sth = $class->sql_OILSFastOrderedSearchLike( $field_list, $class->table, $field, $order->{order_by});
102         }
103         $sth->execute($value);
104         return $sth;
105 }
106
107 sub fast_flesh {
108         my $self = shift;
109         return map $class->construct($_), $self->fast_flesh_sth(@_)->fetchall_hash;
110 }
111
112 sub fast_fieldmapper {
113         my $self = shift;
114         my $id = shift;
115         my $col = shift;
116         my $like = shift;
117         my $options = shift;
118         my $class = ref($self) || $self;
119         my $fm_class = 'Fieldmapper::'.$class;
120         my @fms;
121         $log->debug("fast_fieldmapper() ==> Retrieving $fm_class", INTERNAL);
122         if ($like < 2) {
123                 for my $hash ($self->fast_flesh_sth( $col, "$id", { order_by => $col }, $like )->fetchall_hash) {
124                         my $fm = $fm_class->new;
125                         for my $field ( $fm_class->real_fields ) {
126                                 $fm->$field( $$hash{$field} );
127                         }
128                         push @fms, $fm;
129                 }
130         } else {
131                 my $search_type = 'search';
132                 if ($like == 2) {
133                         $search_type = 'search_fts'
134                 } elsif ($like == 3) {
135                         $search_type = 'search_regex'
136                 }
137
138                 for my $obj ($class->$search_type({ $col => $id}, $options)) {
139                         push @fms, $obj->to_fieldmapper;
140                 }
141         }
142         return @fms;
143 }
144
145 sub retrieve {
146         my $self = shift;
147         my $arg = shift;
148         if (ref($arg) &&
149                 (UNIVERSAL::isa($arg => 'Fieldmapper') ||
150                  UNIVERSAL::isa($arg => 'Class::DBI')) ) {
151                 my ($col) = $self->primary_column;
152                 $log->debug("Using field $col as the primary key", INTERNAL);
153                 $arg = $arg->$col;
154         } elsif (ref $arg) {
155                 my ($col) = $self->primary_column;
156                 $log->debug("Using field $col as the primary key", INTERNAL);
157                 $arg = $arg->{$col};
158         }
159                 
160         $log->debug("Retrieving $self with $arg", INTERNAL);
161         my $rec;
162         try {
163                 $rec = $self->SUPER::retrieve("$arg");
164         } catch Error with {
165                 $log->debug("Could not retrieve $self with $arg! -- ".shift(), DEBUG);
166                 return undef;
167         };
168         return $rec;
169 }
170
171 sub to_fieldmapper {
172         my $obj = shift;
173         my $class = ref($obj) || $obj;
174
175         my $fm_class = 'Fieldmapper::'.$class;
176         my $fm = $fm_class->new;
177
178         if (ref($obj)) {
179                 for my $field ( $fm->real_fields ) {
180                         $fm->$field( ''.$obj->$field );
181                 }
182         }
183
184         return $fm;
185 }
186
187 sub merge {
188         my $self = shift;
189         my $search = shift;
190         my $arg = shift;
191
192         delete $$arg{$_} for (keys %$search);
193
194         $log->debug("CDBI->merge: \$search is $search (".ref($search)." : ".join(',',map{"$_ => $$search{$_}"}keys(%$search)).")",DEBUG);
195         $log->debug("CDBI->merge: \$arg is $arg (".ref($arg)." : ".join(',',map{"$_ => $$arg{$_}"}keys(%$arg)).")",DEBUG);
196
197         my @objs = ($self);
198         @objs = $self->search_where($search) unless (ref $self);
199
200         if (@objs == 1) {
201                 $objs[0]->update($arg);
202                 return $objs[0];
203         } elsif (@objs == 0) {
204                 return $self->create({%$search,%$arg});
205         } else {
206                 throw OpenSRF::EX::WARN ("Non-unique search key for merge.  Perhaps you meant to use remote_update?");
207         }
208 }
209
210 sub remote_update {
211         my $self = shift;
212         my $search = shift;
213         my $arg = shift;
214
215         delete $$arg{$_} for (keys %$search);
216
217         $log->debug("CDBI->remote_update: \$search is $search (".ref($search)." : ".join(',',map{"$_ => $$search{$_}"}keys(%$search)).")",DEBUG);
218         $log->debug("CDBI->remote_update: \$arg is $arg (".ref($arg)." : ".join(',',map{"$_ => $$arg{$_}"}keys(%$arg)).")",DEBUG);
219
220 #       my @objs = $self->search_where($search);
221 #       throw OpenSRF::EX::WARN ("No objects found for remote_update.  Perhaps you meant to use merge?")
222 #               if (@objs == 0);
223
224 #       $_->update($arg) for (@objs);
225 #       return scalar(@objs);
226
227         my @finds = sort keys %$search;
228         my @sets = sort keys %$arg;
229
230         my @find_vals = @$search{@finds};
231         my @set_vals = @$arg{@sets};
232
233         my $sql = 'UPDATE %s SET %s WHERE %s';
234
235         my $table = $self->table;
236         my $set = join(', ', map { "$_=?" } @sets);
237         my $where = join(', ', map { "$_=?" } @finds);
238
239         my $sth = $self->db_Main->prepare(sprintf($sql, $table, $set, $where));
240         $sth->execute(@set_vals,@find_vals);
241         return $sth->rows;
242
243 }
244
245 sub create {
246         my $self = shift;
247         my $arg = shift;
248
249         $log->debug("CDBI->create: \$arg is $arg (".ref($arg)." : ".OpenSRF::Utils::JSON->perl2JSON($arg).")",DEBUG);
250
251         if (ref($arg) && UNIVERSAL::isa($arg => 'Fieldmapper')) {
252                 return $self->create_from_fieldmapper($arg,@_);
253         }
254
255         return $self->SUPER::create($arg,@_);
256 }
257
258 sub create_from_fieldmapper {
259         my $obj = shift;
260         my $fm = shift;
261         my @params = @_;
262
263         $log->debug("Creating node of type ".ref($fm), DEBUG);
264
265         my $class = ref($obj) || $obj;
266         my ($primary) = $class->columns('Primary');
267
268         if (ref($fm) &&UNIVERSAL::isa($fm => 'Fieldmapper')) {
269                 my %hash = map { defined $fm->$_ ?
270                                         ($_ => $fm->$_) :
271                                         ()
272                                 } grep { $_ ne $primary } $class->columns('Essential');
273
274                 if ($class->find_column( 'last_xact_id' )) {
275                         if ($OpenILS::Application::Storage::IGNORE_XACT_ID_FAILURE) {
276                                 $hash{last_xact_id} = 'unknown.'.time.'.'.$$.'.'.rand($$);
277                         } else {
278                                 my $xact_id = $class->current_xact_id;
279                                 throw Error unless ($xact_id);
280                                 $hash{last_xact_id} = $xact_id;
281                         }
282                 }
283
284                 return $class->create( \%hash, @params );
285         } else {
286                 return undef;
287         }
288 }
289
290 sub delete {
291         my $self = shift;
292         my $arg = shift;
293         my $orig = $self;
294
295         my $class = ref($self) || $self;
296
297         $self = $self->retrieve($arg) if (!ref($self));
298         unless (defined $self) {
299                 $log->debug("ARG! Couldn't retrieve record ".$arg->id, DEBUG);
300                 throw OpenSRF::EX::WARN ("ARG! Couldn't retrieve record ");
301         }
302
303         if ($class->find_column( 'last_xact_id' )) {
304                 my $xact_id = $self->current_xact_id;
305                 
306                 throw Error ("Deleting from $class requires a transaction be established")
307                         unless ($xact_id);
308                 
309                 throw Error ("The row you are attempting to delete has been changed since you read it")
310                         unless ( $orig->last_xact_id eq $self->last_xact_id);
311
312                 $self->last_xact_id( $class->current_xact_id );
313                 $self->SUPER::update;
314         }
315
316         $self->SUPER::delete;
317
318         return 1;
319 }
320
321 sub debug_object {
322         my $obj = shift;
323         my $string = '';
324
325         $string .= "Object type:\t".ref($obj)."\n";
326         $string .= "Object string:\t$obj\n";
327
328         if (ref($obj) && UNIVERSAL::isa($obj => 'Fieldmapper')) {
329                 $string .= "Object fields:\n";
330                 for my $col ($obj->real_fields()) {
331                         $string .= "\t$col\t=> ".$obj->$col."\n";
332                 }
333         } elsif (ref($obj) && UNIVERSAL::isa($obj => 'Class::DBI')) {
334                 $string .= "Object cols:\n";
335                 for my $col ($obj->columns('All')) {
336                         $string .= "\t$col\t=> ".$obj->$col."\n";
337                 }
338         } elsif (ref($obj) && UNIVERSAL::isa($obj => 'HASH')) {
339                 $string .= "Object keys and vals:\n";
340                 for my $col (keys %$obj) {
341                         $string .= "\t$col\t=> $$obj{$col}\n";
342                 }
343         }
344
345         $string .= "\n";
346         
347         $log->debug($string,DEBUG);
348 }
349
350
351 sub update {
352         my $self = shift;
353         my $arg = shift;
354
355         $log->debug("Attempting to update using $arg", DEBUG) if ($arg);
356
357         if (ref($arg)) {
358                 $self = $self->modify_from_fieldmapper($arg);
359                 unless (defined $self) {
360                         $log->debug("Modification of $arg seems to have failed....", DEBUG);
361                         return undef;
362                 }
363         }
364
365         $log->debug("Calling Class::DBI->update on modified object $self", DEBUG);
366
367         #debug_object($self);
368
369         return $self->SUPER::update if ($self->is_changed);
370         return 0;
371 }
372
373 sub modify_from_fieldmapper {
374         my $obj = shift;
375         my $fm = shift;
376         my $orig = $obj;
377
378         #debug_object($obj);
379         #debug_object($fm);
380
381         $log->debug("Modifying object using fieldmapper", DEBUG);
382
383         my $class = ref($obj) || $obj;
384         my ($primary) = $class->columns('Primary');
385
386
387         if (!ref($obj)) {
388                 $obj = $class->retrieve($fm);
389                 #debug_object($obj);
390                 unless ($obj) {
391                         $log->debug("Retrieve of $class using $fm (".$fm->id.") failed! -- ".shift(), ERROR);
392                         throw OpenSRF::EX::WARN ("No $class with id of ".$fm->id."!!");
393                 }
394         }
395
396         my %hash;
397         
398         if (ref($fm) and UNIVERSAL::isa($fm => 'Fieldmapper')) {
399                 %hash = map { ($_ => $fm->$_) } grep { $_ ne $primary } $class->columns('Essential');
400                 delete $hash{passwd} if ($fm->isa('Fieldmapper::actor::user'));
401         } else {
402                 %hash = %{$fm};
403         }
404
405         my $au = $obj->autoupdate;
406         $obj->autoupdate(0);
407         
408         #debug_object($obj);
409
410         for my $field ( keys %hash ) {
411                 $obj->$field( $hash{$field} ) if ($obj->$field ne $hash{$field});
412                 $log->debug("Setting field $field on $obj to $hash{$field}",INTERNAL);
413         }
414
415         if ($class->find_column( 'last_xact_id' ) and $obj->is_changed) {
416                 my ($xact_id) = OpenILS::Application::Storage->method_lookup('open-ils.storage.transaction.current')->run();
417                 throw Error ("Updating $class requires a transaction be established")
418                         unless ($xact_id);
419                 throw Error ("The row you are attempting to delete has been changed since you read it")
420                         unless ( $fm->last_xact_id eq $obj->last_xact_id);
421                 $obj->last_xact_id( $xact_id );
422         } else {
423                 $obj->autoupdate($au)
424         }
425
426         return $obj;
427 }
428
429
430
431         #-------------------------------------------------------------------------------
432         actor::user->has_a( home_ou => 'actor::org_unit' );
433         actor::user->has_a( card => 'actor::card' );
434         actor::user->has_a( standing => 'config::standing' );
435         actor::user->has_a( profile => 'permission::grp_tree' );
436         actor::user->has_a( mailing_address => 'actor::user_address' );
437         actor::user->has_a( billing_address => 'actor::user_address' );
438         actor::user->has_a( ident_type => 'config::identification_type' );
439         actor::user->has_a( ident_type2 => 'config::identification_type' );
440         actor::user->has_a( net_access_level => 'config::net_access_level' );
441
442         actor::user_address->has_a( usr => 'actor::user' );
443         
444         actor::card->has_a( usr => 'actor::user' );
445         
446         actor::workstation->has_a( owning_lib => 'actor::org_unit' );
447         actor::org_unit::closed_date->has_a( org_unit => 'actor::org_unit' );
448         actor::org_unit_setting->has_a( org_unit => 'actor::org_unit' );
449
450         actor::usr_note->has_a( usr => 'actor::user' );
451         actor::user->has_many( notes => 'actor::usr_note' );
452
453         actor::user_standing_penalty->has_a( usr => 'actor::user' );
454         actor::user->has_many( standing_penalties => 'actor::user_standing_penalty' );
455
456         actor::org_unit->has_a( parent_ou => 'actor::org_unit' );
457         actor::org_unit->has_a( ou_type => 'actor::org_unit_type' );
458         actor::org_unit->has_a( ill_address => 'actor::org_address' );
459         actor::org_unit->has_a( holds_address => 'actor::org_address' );
460         actor::org_unit->has_a( mailing_address => 'actor::org_address' );
461         actor::org_unit->has_a( billing_address => 'actor::org_address' );
462         actor::org_unit->has_many( children => 'actor::org_unit' => 'parent_ou' );
463         actor::org_unit->has_many( workstations => 'actor::workstation' );
464         actor::org_unit->has_many( closed_dates => 'actor::org_unit::closed_date' );
465         actor::org_unit->has_many( settings => 'actor::org_unit_setting' );
466         #actor::org_unit->might_have( hours_of_operation => 'actor::org_unit::hours_of_operation' );
467
468         actor::org_unit_type->has_a( parent => 'actor::org_unit_type' );
469         actor::org_unit_type->has_many( children => 'actor::org_unit_type' => 'parent' );
470
471         actor::org_address->has_a( org_unit => 'actor::org_unit' );
472         actor::org_unit->has_many( addresses => 'actor::org_address' );
473
474         action::transit_copy->has_a( source => 'actor::org_unit' );
475         action::transit_copy->has_a( dest => 'actor::org_unit' );
476         action::transit_copy->has_a( copy_status => 'config::copy_status' );
477
478         action::hold_transit_copy->has_a( source => 'actor::org_unit' );
479         action::hold_transit_copy->has_a( dest => 'actor::org_unit' );
480         action::hold_transit_copy->has_a( copy_status => 'config::copy_status' );
481         action::hold_transit_copy->has_a( hold => 'action::hold_request' );
482
483         action::hold_request->has_many( transits => 'action::hold_transit_copy' );
484
485         actor::stat_cat_entry->has_a( stat_cat => 'actor::stat_cat' );
486         actor::stat_cat->has_a( owner => 'actor::org_unit' );
487         actor::stat_cat->has_many( entries => 'actor::stat_cat_entry' );
488         actor::stat_cat_entry_user_map->has_a( stat_cat => 'actor::stat_cat' );
489         actor::stat_cat_entry_user_map->has_a( stat_cat_entry => 'actor::stat_cat_entry' );
490         actor::stat_cat_entry_user_map->has_a( target_usr => 'actor::user' );
491
492         asset::stat_cat_entry->has_a( stat_cat => 'asset::stat_cat' );
493         asset::stat_cat->has_a( owner => 'actor::org_unit' );
494         asset::stat_cat->has_many( entries => 'asset::stat_cat_entry' );
495         asset::stat_cat_entry_copy_map->has_a( stat_cat => 'asset::stat_cat' );
496         asset::stat_cat_entry_copy_map->has_a( stat_cat_entry => 'asset::stat_cat_entry' );
497         asset::stat_cat_entry_copy_map->has_a( owning_copy => 'asset::copy' );
498
499         action::survey_response->has_a( usr => 'actor::user' );
500         action::survey_response->has_a( survey => 'action::survey' );
501         action::survey_response->has_a( question => 'action::survey_question' );
502         action::survey_response->has_a( answer => 'action::survey_answer' );
503
504         action::survey_question->has_a( survey => 'action::survey' );
505
506         action::survey_answer->has_a( question => 'action::survey_question' );
507
508         asset::copy_note->has_a( owning_copy => 'asset::copy' );
509         asset::copy_note->has_a( creator => 'actor::user' );
510
511         actor::user->has_many( stat_cat_entries => [ 'actor::stat_cat_entry_user_map' => 'stat_cat_entry' ] );
512         actor::user->has_many( stat_cat_entry_user_maps => 'actor::stat_cat_entry_user_map' );
513
514         asset::copy->has_many( stat_cat_entries => [ 'asset::stat_cat_entry_copy_map' => 'stat_cat_entry' ] );
515         asset::copy->has_many( stat_cat_entry_copy_maps => 'asset::stat_cat_entry_copy_map' );
516
517         asset::copy->has_a( call_number => 'asset::call_number' );
518         asset::copy->has_a( creator => 'actor::user' );
519         asset::copy->has_a( editor => 'actor::user' );
520         asset::copy->has_a( status => 'config::copy_status' );
521         asset::copy->has_a( location => 'asset::copy_location' );
522         asset::copy->has_a( circ_lib => 'actor::org_unit' );
523
524         asset::call_number_note->has_a( call_number => 'asset::call_number' );
525
526         asset::call_number->has_a( record => 'biblio::record_entry' );
527         asset::call_number->has_a( creator => 'actor::user' );
528         asset::call_number->has_a( editor => 'actor::user' );
529         asset::call_number->has_a( owning_lib => 'actor::org_unit' );
530
531         authority::record_note->has_a( record => 'authority::record_entry' );
532         biblio::record_note->has_a( record => 'biblio::record_entry' );
533         
534         authority::record_entry->has_a( creator => 'actor::user' );
535         authority::record_entry->has_a( editor => 'actor::user' );
536         biblio::record_entry->has_a( creator => 'actor::user' );
537         biblio::record_entry->has_a( editor => 'actor::user' );
538         
539         metabib::metarecord->has_a( master_record => 'biblio::record_entry' );
540         
541         authority::record_descriptor->has_a( record => 'authority::record_entry' );
542         metabib::record_descriptor->has_a( record => 'biblio::record_entry' );
543         
544         authority::full_rec->has_a( record => 'authority::record_entry' );
545         metabib::full_rec->has_a( record => 'biblio::record_entry' );
546         
547         metabib::title_field_entry->has_a( source => 'biblio::record_entry' );
548         metabib::title_field_entry->has_a( field => 'config::metabib_field' );
549
550         metabib::identifier_field_entry->has_a( source => 'biblio::record_entry' );
551         metabib::identifier_field_entry->has_a( field => 'config::metabib_field' );
552         
553         metabib::author_field_entry->has_a( source => 'biblio::record_entry' );
554         metabib::author_field_entry->has_a( field => 'config::metabib_field' );
555         
556         metabib::subject_field_entry->has_a( source => 'biblio::record_entry' );
557         metabib::subject_field_entry->has_a( field => 'config::metabib_field' );
558         
559         metabib::keyword_field_entry->has_a( source => 'biblio::record_entry' );
560         metabib::keyword_field_entry->has_a( field => 'config::metabib_field' );
561         
562         metabib::series_field_entry->has_a( source => 'biblio::record_entry' );
563         metabib::series_field_entry->has_a( field => 'config::metabib_field' );
564         
565         metabib::metarecord_source_map->has_a( metarecord => 'metabib::metarecord' );
566         metabib::metarecord_source_map->has_a( source => 'biblio::record_entry' );
567
568         action::circulation->has_a( usr => 'actor::user' );
569         actor::user->has_many( circulations => 'action::circulation' => 'usr' );
570
571         booking::resource_attr_map->has_a( resource => 'booking::resource' );
572
573         booking::resource->has_a( owner => 'actor::org_unit' );
574         booking::resource->has_a( type => 'booking::resource_type' );
575         booking::resource_type->has_a( owner => 'actor::org_unit' );
576
577         booking::reservation->has_a( usr => 'actor::user' );
578         actor::user->has_many( reservations => 'booking::reservation' => 'usr' );
579         
580         action::circulation->has_a( circ_staff => 'actor::user' );
581         actor::user->has_many( performed_circulations => 'action::circulation' => 'circ_staff' );
582
583         action::circulation->has_a( checkin_staff => 'actor::user' );
584         actor::user->has_many( checkins => 'action::circulation' => 'checkin_staff' );
585
586         action::circulation->has_a( target_copy => 'asset::copy' );
587         asset::copy->has_many( circulations => 'action::circulation' => 'target_copy' );
588
589         booking::reservation->has_a( pickup_lib => 'actor::org_unit' );
590
591         action::circulation->has_a( circ_lib => 'actor::org_unit' );
592         actor::org_unit->has_many( circulations => 'action::circulation' => 'circ_lib' );
593         
594         action::circulation->has_a( checkin_lib => 'actor::org_unit' );
595         actor::org_unit->has_many( checkins => 'action::circulation' => 'checkin_lib' );
596
597         money::billable_transaction->has_a( usr => 'actor::user' );
598         #money::billable_transaction->might_have( circulation => 'action::circulation' );
599         #money::billable_transaction->might_have( grocery => 'money::grocery' );
600         actor::user->has_many( billable_transactions => 'action::circulation' => 'usr' );
601         
602         
603         #-------------------------------------------------------------------------------
604         actor::user->has_many( survey_responses => 'action::survey_response' );
605         actor::user->has_many( addresses => 'actor::user_address' );
606         actor::user->has_many( cards => 'actor::card' );
607
608         actor::org_unit->has_many( users => 'actor::user' );
609
610         action::survey->has_many( questions => 'action::survey_question' );
611         action::survey->has_many( responses => 'action::survey_response' );
612         
613         action::survey_question->has_many( answers => 'action::survey_answer' );
614         action::survey_question->has_many( responses => 'action::survey_response' );
615
616         action::survey_answer->has_many( responses => 'action::survey_response' );
617
618         asset::copy->has_many( notes => 'asset::copy_note' );
619         asset::call_number->has_many( copies => 'asset::copy' );
620         asset::call_number->has_many( notes => 'asset::call_number_note' );
621
622         authority::record_entry->has_many( record_descriptor => 'authority::record_descriptor' );
623         authority::record_entry->has_many( notes => 'authority::record_note' );
624
625         biblio::record_entry->has_many( record_descriptor => 'metabib::record_descriptor' );
626         biblio::record_entry->has_many( notes => 'biblio::record_note' );
627         biblio::record_entry->has_many( call_numbers => 'asset::call_number' );
628         biblio::record_entry->has_many( full_record_entries => 'metabib::full_rec' );
629         biblio::record_entry->has_many( title_field_entries => 'metabib::title_field_entry' );
630         biblio::record_entry->has_many( identifier_field_entries => 'metabib::identifier_field_entry' );
631         biblio::record_entry->has_many( author_field_entries => 'metabib::author_field_entry' );
632         biblio::record_entry->has_many( subject_field_entries => 'metabib::subject_field_entry' );
633         biblio::record_entry->has_many( keyword_field_entries => 'metabib::keyword_field_entry' );
634         biblio::record_entry->has_many( series_field_entries => 'metabib::series_field_entry' );
635
636         metabib::metarecord->has_many( source_records => [ 'metabib::metarecord_source_map' => 'source'] );
637         biblio::record_entry->has_many( metarecords => [ 'metabib::metarecord_source_map' => 'metarecord'] );
638
639         money::billing->has_a( xact => 'money::billable_transaction' );
640         money::payment->has_a( xact => 'money::billable_transaction' );
641
642         money::billable_transaction->has_many( billings => 'money::billing' );
643         money::billable_transaction->has_many( payments => 'money::payment' );
644
645         action::circulation->has_many( billings => 'money::billing' => 'xact' );
646         action::circulation->has_many( payments => 'money::payment' => 'xact' );
647         #action::circulation->might_have( billable_transaction => 'money::billable_transaction' );
648         #action::open_circulation->might_have( circulation => 'action::circulation' );
649
650         booking::reservation->has_many( billings => 'money::billing' => 'xact' );
651         booking::reservation->has_many( payments => 'money::payment' => 'xact' );
652
653         action::in_house_use->has_a( org_unit => 'actor::org_unit' );
654         action::in_house_use->has_a( staff => 'actor::user' );
655         action::in_house_use->has_a( item => 'asset::copy' );
656
657         action::non_cataloged_circulation->has_a( circ_lib => 'actor::org_unit' );
658         action::non_cataloged_circulation->has_a( item_type => 'config::non_cataloged_type' );
659         action::non_cataloged_circulation->has_a( patron => 'actor::user' );
660         action::non_cataloged_circulation->has_a( staff => 'actor::user' );
661
662         money::grocery->has_many( billings => 'money::billing' => 'xact' );
663         money::grocery->has_many( payments => 'money::payment' => 'xact' );
664         #money::grocery->might_have( billable_transaction => 'money::billable_transaction' );
665
666         #money::payment->might_have( cash_payment => 'money::cash_payment' );
667         #money::payment->might_have( check_payment => 'money::check_payment' );
668         #money::payment->might_have( credit_card_payment => 'money::credit_card_payment' );
669         #money::payment->might_have( forgive_payment => 'money::forgive_payment' );
670         #money::payment->might_have( work_payment => 'money::work_payment' );
671         #money::payment->might_have( credit_payment => 'money::credit_payment' );
672
673         money::cash_payment->has_a( xact => 'money::billable_transaction' );
674         money::cash_payment->has_a( accepting_usr => 'actor::user' );
675         #money::cash_payment->might_have( payment => 'money::payment' );
676
677         money::check_payment->has_a( xact => 'money::billable_transaction' );
678         money::check_payment->has_a( accepting_usr => 'actor::user' );
679         #money::check_payment->might_have( payment => 'money::payment' );
680
681         money::credit_card_payment->has_a( xact => 'money::billable_transaction' );
682         money::credit_card_payment->has_a( accepting_usr => 'actor::user' );
683         #money::credit_card_payment->might_have( payment => 'money::payment' );
684
685         money::forgive_payment->has_a( xact => 'money::billable_transaction' );
686         money::forgive_payment->has_a( accepting_usr => 'actor::user' );
687         #money::forgive_payment->might_have( payment => 'money::payment' );
688
689         money::work_payment->has_a( xact => 'money::billable_transaction' );
690         money::work_payment->has_a( accepting_usr => 'actor::user' );
691         #money::work_payment->might_have( payment => 'money::payment' );
692
693         money::goods_payment->has_a( xact => 'money::billable_transaction' );
694         money::goods_payment->has_a( accepting_usr => 'actor::user' );
695         #money::goods_payment->might_have( payment => 'money::payment' );
696
697         money::credit_payment->has_a( xact => 'money::billable_transaction' );
698         money::credit_payment->has_a( accepting_usr => 'actor::user' );
699         #money::credit_payment->might_have( payment => 'money::payment' );
700
701         permission::grp_tree->has_a( parent => 'permission::grp_tree' );
702         permission::grp_tree->has_many( children => 'permission::grp_tree' => 'parent' );
703
704         permission::grp_perm_map->has_a( grp => 'permission::grp_tree' );
705         permission::grp_perm_map->has_a(  perm => 'permission::perm_list' );
706         permission::grp_perm_map->has_a(  depth => 'actor::org_unit_type' );
707         
708         permission::usr_perm_map->has_a( usr => 'actor::user' );
709         permission::usr_perm_map->has_a(  perm => 'permission::perm_list' );
710         permission::usr_perm_map->has_a(  depth => 'actor::org_unit_type' );
711         
712         permission::usr_grp_map->has_a(  usr => 'actor::user' );
713         permission::usr_grp_map->has_a(  grp => 'permission::grp_tree' );
714
715         action::hold_notification->has_a(  hold => 'action::hold_request' );
716         
717         action::hold_copy_map->has_a(  hold => 'action::hold_request' );
718         action::hold_copy_map->has_a(  target_copy => 'asset::copy' );
719
720         action::unfulfilled_hold_list->has_a(  current_copy => 'asset::copy' );
721         action::unfulfilled_hold_list->has_a(  hold => 'action::hold_request' );
722         action::unfulfilled_hold_list->has_a(  circ_lib => 'actor::org_unit' );
723
724         action::hold_request->has_a(  current_copy => 'asset::copy' );
725         action::hold_request->has_a(  requestor => 'actor::user' );
726         action::hold_request->has_a(  usr => 'actor::user' );
727         action::hold_request->has_a(  fulfillment_staff => 'actor::user' );
728         action::hold_request->has_a(  pickup_lib => 'actor::org_unit' );
729         action::hold_request->has_a(  request_lib => 'actor::org_unit' );
730         action::hold_request->has_a(  fulfillment_lib => 'actor::org_unit' );
731         action::hold_request->has_a(  selection_ou => 'actor::org_unit' );
732
733         action::hold_request->has_many(  notifications => 'action::hold_notification' );
734         action::hold_request->has_many(  eligible_copies => [ 'action::hold_copy_map' => 'target_copy' ] );
735
736         asset::copy->has_many(  holds => [ 'action::hold_copy_map' => 'hold' ] );
737
738         container::biblio_record_entry_bucket->has_a( owner => 'actor::user' );
739         container::biblio_record_entry_bucket_item->has_a( bucket => 'container::biblio_record_entry_bucket' );
740         container::biblio_record_entry_bucket_item->has_a( target_biblio_record_entry => 'biblio::record_entry' );
741         container::biblio_record_entry_bucket->has_many( items => 'container::biblio_record_entry_bucket_item' );
742
743         container::user_bucket->has_a( owner => 'actor::user' );
744         container::user_bucket_item->has_a( bucket => 'container::user_bucket' );
745         container::user_bucket_item->has_a( target_user => 'actor::user' );
746         container::user_bucket->has_many( items => 'container::user_bucket_item' );
747
748         container::call_number_bucket->has_a( owner => 'actor::user' );
749         container::call_number_bucket_item->has_a( bucket => 'container::call_number_bucket' );
750         container::call_number_bucket_item->has_a( target_call_number => 'asset::call_number' );
751         container::call_number_bucket->has_many( items => 'container::call_number_bucket_item' );
752
753         container::copy_bucket->has_a( owner => 'actor::user' );
754         container::copy_bucket_item->has_a( bucket => 'container::copy_bucket' );
755         container::copy_bucket_item->has_a( target_copy => 'asset::copy' );
756         container::copy_bucket->has_many( items => 'container::copy_bucket_item' );
757
758
759 1;