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