1 package FS::part_event_condition;
4 use vars qw( @ISA $DEBUG @SKIP_CONDITION_SQL );
5 use FS::UID qw( dbh driver_name );
6 use FS::Record qw( qsearch qsearchs );
8 use FS::part_event; #for order_conditions_sql...
10 @ISA = qw( FS::option_Common ); # FS::Record );
13 @SKIP_CONDITION_SQL = ();
17 FS::part_event_condition - Object methods for part_event_condition records
21 use FS::part_event_condition;
23 $record = new FS::part_event_condition \%hash;
24 $record = new FS::part_event_condition { 'column' => 'value' };
26 $error = $record->insert;
28 $error = $new_record->replace($old_record);
30 $error = $record->delete;
32 $error = $record->check;
36 An FS::part_event_condition object represents an event condition.
37 FS::part_event_condition inherits from FS::Record. The following fields are
42 =item eventconditionnum - primary key
44 =item eventpart - Event definition (see L<FS::part_event>)
46 =item conditionname - Condition name - defines which FS::part_event::Condition::I<conditionname> evaluates this condition
56 Creates a new event. To add the example to the database, see L<"insert">.
58 Note that this stores the hash reference, not a distinct copy of the hash it
59 points to. You can ask the object for a copy with the I<hash> method.
63 # the new method can be inherited from FS::Record, if a table method is defined
65 sub table { 'part_event_condition'; }
67 =item insert [ HASHREF | OPTION => VALUE ... ]
69 Adds this record to the database. If there is an error, returns the error,
70 otherwise returns false.
72 If a list or hash reference of options is supplied, part_event_condition_option
73 records are created (see L<FS::part_event_condition_option>).
77 # the insert method can be inherited from FS::Record
81 Delete this record from the database.
85 # the delete method can be inherited from FS::Record
87 =item replace OLD_RECORD [ HASHREF | OPTION => VALUE ... ]
89 Replaces the OLD_RECORD with this one in the database. If there is an error,
90 returns the error, otherwise returns false.
92 If a list or hash reference of options is supplied, part_event_condition_option
93 records are created or modified (see L<FS::part_event_condition_option>).
97 # the replace method can be inherited from FS::Record
101 Checks all fields to make sure this is a valid example. If there is
102 an error, returns the error, otherwise returns false. Called by the insert
107 # the check method should currently be supplied - FS::Record contains some
108 # data checking routines
114 $self->ut_numbern('eventconditionnum')
115 || $self->ut_foreign_key('eventpart', 'part_event', 'eventpart')
116 || $self->ut_alpha('conditionname')
118 return $error if $error;
120 #XXX check conditionname to make sure a module exists?
121 # well it'll die in _rebless...
129 Reblesses the object into the FS::part_event::Condition::CONDITIONNAME class,
130 where CONDITIONNAME is the object's I<conditionname> field.
136 my $conditionname = $self->conditionname;
137 #my $class = ref($self). "::$conditionname";
138 my $class = "FS::part_event::Condition::$conditionname";
141 bless($self, $class); #unless $@;
151 =item conditions [ EVENTTABLE ]
153 Return information about the available conditions. If an eventtable is
154 specified, only return information about conditions available for that
157 Information is returned as key-value pairs. Keys are condition names. Values
158 are hashrefs with the following keys:
166 # =item default_weight
172 See L<FS::part_event::Condition> for more information.
176 #false laziness w/part_event.pm
177 #some false laziness w/part_export & part_pkg
179 foreach my $INC ( @INC ) {
180 foreach my $file ( glob("$INC/FS/part_event/Condition/*.pm") ) {
181 warn "attempting to load Condition from $file\n" if $DEBUG;
182 $file =~ /\/(\w+)\.pm$/ or do {
183 warn "unrecognized file in $INC/FS/part_event/Condition/: $file\n";
187 my $fullmod = "FS::part_event::Condition::$mod";
188 if ( $fullmod =~ /_(Mixin|Common)$/ ) {
189 #warn "skipping $1 class $fullmod\n";
192 eval "use $fullmod;";
194 die "error using $fullmod (skipping): $@\n" if $@;
195 #warn "error using $fullmod (skipping): $@\n" if $@;
198 if ( $fullmod->disabled ) {
199 warn "$fullmod is disabled; skipping\n";
202 #my $full_condition_sql = $fullmod. '::condition_sql';
203 my $condition_sql_coderef = sub { $fullmod->condition_sql(@_) };
204 my $order_sql_coderef = $fullmod->can('order_sql')
205 ? sub { $fullmod->order_sql(@_) }
207 $conditions{$mod} = {
208 ( map { $_ => $fullmod->$_() }
209 qw( description eventtable_hashref
210 implicit_flag remove_warning
214 #option_fields_hashref
216 'option_fields' => [ $fullmod->option_fields() ],
217 'condition_sql' => $condition_sql_coderef,
218 'order_sql' => $order_sql_coderef,
224 my( $class, $eventtable ) = @_;
226 map { $_ => $conditions{$_} }
227 sort {$conditions{$a}->{'description'} cmp $conditions{$b}->{'description'}}
228 $class->all_conditionnames( $eventtable )
233 =item all_conditionnames [ EVENTTABLE ]
235 Returns a list of just the condition names
239 sub all_conditionnames {
240 my ( $class, $eventtable ) = @_;
242 grep { !$eventtable || $conditions{$_}->{'eventtable_hashref'}{$eventtable} }
246 =item join_conditions_sql [ EVENTTABLE [, OPTIONS ] ]
248 Returns an SQL fragment selecting joining all condition options for an event as
249 tables titled "cond_I<conditionname>". Typically used in conjunction with
250 B<where_conditions_sql>. OPTIONS should include 'time', the time to use
251 in testing event conditions.
255 sub join_conditions_sql {
256 my ( $class, $eventtable, %options ) = @_;
260 "LEFT JOIN part_event_condition AS cond_$_".
261 " ON ( part_event.eventpart = cond_$_.eventpart".
262 " AND cond_$_.conditionname = ". dbh->quote($_).
265 map $_->[0], $class->_where_conditions( $eventtable, %options )
271 =item where_conditions_sql [ EVENTTABLE [ , OPTION => VALUE, ... ] ]
273 Returns an SQL fragment to select events which have unsatisfied conditions.
274 Must be used in conjunction with B<join_conditions_sql>.
276 The only current option is "time", the current time (or "pretend" current time
277 as passed to freeside-daily), as a UNIX timestamp.
281 sub where_conditions_sql {
282 my ( $class, $eventtable, %options ) = @_;
285 map { my $conditionname = $_->[0];
287 "( cond_$conditionname.conditionname IS NULL OR $sql )";
289 $class->_where_conditions( $eventtable, %options )
293 sub _where_conditions {
294 my ( $class, $eventtable, %options ) = @_;
296 my $time = $options{'time'};
298 my %conditions = $class->conditions( $eventtable );
300 grep { $_->[1] !~ /^\s*true\s*$/i
301 || $conditions{ $_->[0] }->{order_sql}
304 my $conditionname = $_;
305 my $coderef = $conditions{$conditionname}->{condition_sql};
306 #die "$coderef is not a CODEREF" unless ref($coderef) eq 'CODE';
307 my $sql = &$coderef( $eventtable, 'time' => $time,
308 'driver_name' => driver_name(),
312 grep { my $cond = $_;
313 ! grep { $_ eq $cond } @SKIP_CONDITION_SQL
318 =item order_conditions_sql [ EVENTTABLE ]
320 Returns an SQL fragment to order selected events. Must be used in conjunction
321 with B<join_conditions_sql>.
325 sub order_conditions_sql {
326 my( $class, $eventtable ) = @_;
328 my %conditions = $class->conditions( $eventtable );
330 my $eventtables = join(' ', FS::part_event->eventtables_runorder);
332 my $order_by = join(', ',
333 "position( part_event.eventtable in ' $eventtables ')",
335 my $conditionname = $_;
336 my $coderef = $conditions{$conditionname}->{order_sql};
337 my $sql = &$coderef( $eventtable );
338 "CASE WHEN cond_$conditionname.conditionname IS NULL
344 sort { $conditions{$a}->{order_sql_weight}
345 <=> $conditions{$b}->{order_sql_weight}
347 grep { $conditions{$_}->{order_sql} }
353 "ORDER BY $order_by";
357 sub _upgrade_data { #class method
358 my ($class, %opts) = @_;
360 foreach my $part_event_condition (
361 qsearch('part_event_condition', { 'conditionname' => 'payby' } )
364 my $payby = $part_event_condition->option('payby');
366 if ( scalar( keys %$payby ) == 1 && ( $payby->{CARD} || $payby->{CHEK} )
367 or scalar( keys %$payby ) == 2 && ( $payby->{CARD} && $payby->{CHEK} )
371 $part_event_condition->conditionname('has_cust_payby_auto');
373 } elsif ( $payby->{'BILL'} && ! $payby->{'CARD'} && ! $payby->{'CHEK'} ) {
375 $part_event_condition->conditionname('hasnt_cust_payby_auto');
379 die 'Unable to automatically convert payby condition for event #'.
380 $part_event_condition->eventpart. "\n";
384 my $error = $part_event_condition->replace;
385 die $error if $error;
397 L<FS::part_event::Condition>, L<FS::part_event>, L<FS::Record>, schema.html from
398 the base documentation.