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