1 package FS::part_event;
4 use vars qw( @ISA $DEBUG );
6 use FS::Record qw( dbh qsearch qsearchs );
10 use FS::part_event_option;
11 use FS::part_event_condition;
15 @ISA = qw( FS::m2name_Common FS::option_Common ); # FS::Record );
20 FS::part_event - Object methods for part_event records
26 $record = new FS::part_event \%hash;
27 $record = new FS::part_event { 'column' => 'value' };
29 $error = $record->insert( { 'option' => 'value' } );
30 $error = $record->insert( \%options );
32 $error = $new_record->replace($old_record);
34 $error = $record->delete;
36 $error = $record->check;
38 $error = $record->do_event( $direct_object );
42 An FS::part_event object represents an event definition - a billing, collection
43 or other callback which is triggered when certain customer, invoice, package or
44 other conditions are met. FS::part_event inherits from FS::Record. The
45 following fields are currently supported:
49 =item eventpart - primary key
51 =item agentnum - Optional agentnum (see L<FS::agent>)
53 =item event - event name
55 =item eventtable - table name against which this event is triggered; currently "cust_bill" (the traditional invoice events), "cust_main" (customer events) or "cust_pkg (package events) (or "cust_statement")
57 =item check_freq - how often events of this type are checked; currently "1d" (daily) and "1m" (monthly) are recognized. Note that the apprioriate freeside-daily and/or freeside-monthly cron job needs to be in place.
59 =item weight - ordering for events
61 =item action - event action (like part_bill_event.plan - eventcode plan)
63 =item disabled - Disabled flag, empty or `Y'
73 Creates a new invoice event definition. To add the invoice event definition to
74 the database, see L<"insert">.
76 Note that this stores the hash reference, not a distinct copy of the hash it
77 points to. You can ask the object for a copy with the I<hash> method.
81 # the new method can be inherited from FS::Record, if a table method is defined
83 sub table { 'part_event'; }
85 =item insert [ HASHREF ]
87 Adds this record to the database. If there is an error, returns the error,
88 otherwise returns false.
90 If a list or hash reference of options is supplied, part_export_option records
91 are created (see L<FS::part_event_option>).
95 # the insert method can be inherited from FS::Record
99 Delete this record from the database.
103 # the delete method can be inherited from FS::Record
105 =item replace OLD_RECORD [ HASHREF | OPTION => VALUE ... ]
107 Replaces the OLD_RECORD with this one in the database. If there is an error,
108 returns the error, otherwise returns false.
110 If a list or hash reference of options is supplied, part_event_option
111 records are created or modified (see L<FS::part_event_option>).
115 # the replace method can be inherited from FS::Record
119 Checks all fields to make sure this is a valid invoice event definition. If
120 there is an error, returns the error, otherwise returns false. Called by the
121 insert and replace methods.
125 # the check method should currently be supplied - FS::Record contains some
126 # data checking routines
131 $self->weight(0) unless $self->weight;
134 $self->ut_numbern('eventpart')
135 || $self->ut_text('event')
136 || $self->ut_enum('eventtable', [ $self->eventtables ] )
137 || $self->ut_enum('check_freq', [ '1d', '1m' ])
138 || $self->ut_number('weight')
139 || $self->ut_alpha('action')
140 || $self->ut_enum('disabled', [ '', 'Y' ] )
141 || $self->ut_agentnum_acl('agentnum', 'Edit global billing events')
143 return $error if $error;
145 #XXX check action to make sure a module exists?
146 # well it'll die in _rebless...
153 Reblesses the object into the FS::part_event::Action::ACTION class, where
154 ACTION is the object's I<action> field.
160 my $action = $self->action or return $self;
161 #my $class = ref($self). "::$action";
162 my $class = "FS::part_event::Action::$action";
165 bless($self, $class); # unless $@;
169 =item part_event_condition
171 Returns the conditions associated with this event, as FS::part_event_condition
172 objects (see L<FS::part_event_condition>)
176 sub part_event_condition {
178 qsearch( 'part_event_condition', { 'eventpart' => $self->eventpart } );
181 =item new_cust_event OBJECT
183 Creates a new customer event (see L<FS::cust_event>) for the provided object.
188 my( $self, $object ) = @_;
190 confess "**** $object is not a ". $self->eventtable
191 if ref($object) ne "FS::". $self->eventtable;
193 my $pkey = $object->primary_key;
196 'eventpart' => $self->eventpart,
197 'tablenum' => $object->$pkey(),
198 '_date' => time, #i think we always want the real "now" here.
203 #surely this doesn't work
204 sub reasontext { confess "part_event->reasontext deprecated"; }
207 #Returns the text of any reason associated with this event.
213 # my $r = qsearchs('reason', { 'reasonnum' => $self->reason });
223 Returns the associated agent for this event, if any, as an FS::agent object.
229 qsearchs('agent', { 'agentnum' => $self->agentnum } );
234 Returns the alternate invoice template name, if any, or false if there is
235 no alternate template for this event.
242 if ( $self->action =~ /^cust_bill_send_(alternate|agent)$/
243 && ( $self->option('agent_templatename')
244 || $self->option('templatename') )
247 $self->option('agent_templatename')
248 || $self->option('templatename');
255 =item targets OPTIONS
257 Returns all objects (of type C<FS::eventtable>, for this object's
258 C<eventtable>) eligible for processing under this event, as of right now.
259 The L<FS::cust_event> object used to test event conditions will be
260 included in each object as the 'cust_event' pseudo-field.
262 This is not used in normal event processing (which is done on a
263 per-customer basis to control timing of pre- and post-billing events)
264 but can be useful when configuring events.
271 my $time = $opt{'time'} || time;
273 my $eventpart = $self->eventpart;
274 $eventpart =~ /^\d+$/ or die "bad eventpart $eventpart";
275 my $eventtable = $self->eventtable;
277 # find all objects that meet the conditions for this part_event
279 # this is the 'object' side of the FROM clause
280 if ( $eventtable ne 'cust_main' ) {
282 #($self->eventtables_cust_join->{$eventtable} || '') . #2.3
283 ' LEFT JOIN cust_main USING (custnum) ';
286 # this is the 'event' side
287 my $join = FS::part_event_condition->join_conditions_sql( $eventtable );
288 my $where = FS::part_event_condition->where_conditions_sql( $eventtable,
292 " INNER JOIN part_event ON ( part_event.eventpart = $eventpart ) $join";
294 $where .= ' AND cust_main.agentnum = '.$self->agentnum
296 # don't enforce check_freq since this is a special, out-of-order check
297 # and don't enforce disabled because we want to be able to see targets
298 # for a disabled event
300 my @objects = qsearch({
301 table => $eventtable,
304 extra_sql => "WHERE $where",
307 foreach my $object ( @objects ) {
308 my $cust_event = $self->new_cust_event($object);
309 next unless $cust_event->test_conditions('time' => $time);
311 $object->set('cust_event', $cust_event);
312 push @tested_objects, $object;
323 =item eventtable_labels
325 Returns a hash reference of labels for eventtable values,
326 i.e. 'cust_main'=>'Customer'
330 sub eventtable_labels {
333 tie my %hash, 'Tie::IxHash',
334 'cust_pkg' => 'Package',
335 'cust_bill' => 'Invoice',
336 'cust_main' => 'Customer',
337 'cust_pay_batch' => 'Batch payment',
338 'cust_statement' => 'Statement', #too general a name here? "Invoice group"?
344 =item eventtable_pkey_sql
346 Returns a hash reference of full SQL primary key names for eventtable values,
347 i.e. 'cust_main'=>'cust_main.custnum'
351 sub eventtable_pkey_sql {
354 my $hashref = $class->eventtable_pkey;
356 my %hash = map { $_ => "$_.". $hashref->{$_} } keys %$hashref;
361 =item eventtable_pkey
363 Returns a hash reference of full SQL primary key names for eventtable values,
364 i.e. 'cust_main'=>'custnum'
368 sub eventtable_pkey {
372 'cust_main' => 'custnum',
373 'cust_bill' => 'invnum',
374 'cust_pkg' => 'pkgnum',
375 'cust_pay_batch' => 'paybatchnum',
376 'cust_statement' => 'statementnum',
382 Returns a list of eventtable values (default ordering; suited for display).
388 my $eventtables = $class->eventtable_labels;
392 =item eventtables_runorder
394 Returns a list of eventtable values (run order).
398 sub eventtables_runorder {
399 shift->eventtables; #same for now
402 =item check_freq_labels
404 Returns a hash reference of labels for check_freq values,
409 sub check_freq_labels {
419 =item actions [ EVENTTABLE ]
421 Return information about the available actions. If an eventtable is specified,
422 only return information about actions available for that eventtable.
424 Information is returned as key-value pairs. Keys are event names. Values are
425 hashrefs with the following keys:
431 =item eventtable_hashref
441 See L<FS::part_event::Action> for more information.
445 #false laziness w/part_event_condition.pm
446 #some false laziness w/part_export & part_pkg
448 foreach my $INC ( @INC ) {
449 foreach my $file ( glob("$INC/FS/part_event/Action/*.pm") ) {
450 warn "attempting to load Action from $file\n" if $DEBUG;
451 $file =~ /\/(\w+)\.pm$/ or do {
452 warn "unrecognized file in $INC/FS/part_event/Action/: $file\n";
456 eval "use FS::part_event::Action::$mod;";
458 die "error using FS::part_event::Action::$mod (skipping): $@\n" if $@;
459 #warn "error using FS::part_event::Action::$mod (skipping): $@\n" if $@;
463 ( map { $_ => "FS::part_event::Action::$mod"->$_() }
464 qw( description eventtable_hashref default_weight deprecated )
465 #option_fields_hashref
467 'option_fields' => [ "FS::part_event::Action::$mod"->option_fields() ],
473 my( $class, $eventtable ) = @_;
475 map { $_ => $actions{$_} }
476 sort { $actions{$a}->{'default_weight'}<=>$actions{$b}->{'default_weight'} }
477 $class->all_actions( $eventtable )
482 =item all_actions [ EVENTTABLE ]
484 Returns a list of just the action names
489 my ( $class, $eventtable ) = @_;
491 grep { !$eventtable || $actions{$_}->{'eventtable_hashref'}{$eventtable} }
499 L<FS::part_event_option>, L<FS::part_event_condition>, L<FS::cust_main>,
500 L<FS::cust_pkg>, L<FS::cust_bill>, L<FS::cust_bill_event>, L<FS::Record>,
501 schema.html from the base documentation.