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