2 'name' => 'Billing event definition',
3 'table' => 'part_event',
6 { field => 'eventtable',
8 options => [ FS::part_event->eventtables ],
9 labels => $eventtable_labels,
10 onchange => 'eventtable_changed',
12 { field => 'agentnum',
13 type => 'select-agent',
14 disable_empty => $disable_empty_agent,
16 { field => 'check_freq',
18 options => [ '1d', '1m' ],
19 labels => $check_freq_labels,
21 { field => 'disabled',
25 { field => '_initialize',
27 onchange => '_initialize_changed',
31 value => 'Event Conditions',
33 { field => 'conditionname',
34 type => 'selectlayers',
35 options => [ keys %all_conditions ],
36 labels => \%condition_labels,
37 onchange => 'condition_changed(what);',
38 layer_fields => \%condition_fields,
39 layer_values_callback => $condition_layer_values,
40 html_between => n_a('action'),
41 m2name_table => 'part_event_condition',
42 m2name_namecol => 'conditionname',
43 m2_label => 'Condition',
44 m2_new_default => \@implicit_condition_objs,
45 m2_error_callback => $condition_error_callback,
46 m2_remove_warnings => \%condition_remove_warnings,
47 m2_new_js => 'condition_repop',
48 m2_remove_js => 'condition_add',
51 value => 'Event Action',
54 type => 'selectlayers',
55 options => [ keys %all_actions ],
56 labels => \%action_labels,
57 onchange => 'action_changed(what);',
58 layer_fields => \%action_fields,
59 layer_values_callback => $action_layer_values,
60 html_between => n_a('action'),
64 'eventpart' => 'Event',
65 'event' => 'Event name',
66 'eventtable' => 'Type',
67 'agentnum' => 'Agent',
68 'check_freq' => 'Check frequency',
69 'disabled' => 'Disable event',
71 'conditionname' => 'Add new condition',
74 '_initialize' => 'Initialize event',
76 'viewall_dir' => 'browse',
77 'new_callback' => sub { #start empty for new events only
78 my( $cgi, $object, $fields_listref ) = @_;
79 unshift @{ $fields_listref->[1]{'options'} }, '';
81 'error_callback' => $error_callback,
84 'agent_null_right' => 'Edit global billing events',
86 <SCRIPT TYPE="text/javascript">
88 window.onload = function () { eventtable_changed(document.getElementById('eventtable')) };
91 function eventtable_changed(what) {
94 alert('eventtable_changed called on ' + what );
97 var eventtable = what.options[what.selectedIndex].value;
99 alert ("eventtable: " + eventtable);
101 var eventdesc = what.options[what.selectedIndex].text;
103 //remove the ** Select type **
104 if ( what.options[0].value == '' && notonload++ > 0 ) {
105 what.options[0] = null;
109 // XXX gray out conditions that can't apply (in addition to the warning)?
113 // update condition selects
116 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
117 var cond_id = 'conditionname' + cnum;
118 var cond_select = document.getElementById(cond_id);
121 alert('updating ' + cond_id);
124 // save off the current value
125 var conditionname = cond_select.options[cond_select.selectedIndex].value;
126 var cond_desc = cond_select.options[cond_select.selectedIndex].text;
128 var seen_condition = condition_repop(cond_select);
130 var warning = document.getElementById(cond_id + '_warning');
132 alert('turning off warning; setting style.display of '+ cond_id +
133 '_warning (' + warning + ') to none');
135 warning.style.display = 'none';
137 if ( ! seen_condition && conditionname != '' ) {
138 // add the current (not valid) condition back
139 opt(cond_select, conditionname, cond_desc, true );
140 if ( ! condition_is_implicit(conditionname) ) {
141 cond_select.parentNode.parentNode.style.display = '';
142 cond_select.disabled = '';
143 // turn on a warning and gray out the condition row
145 alert('turning on warning; setting style.display of '+ cond_id +
146 '_warning (' + warning + ') to ""');
148 warning.innerHTML = 'Not applicable to ' + eventdesc + ' events';
149 warning.style.display = '';
151 if ( ! condition_in_eventtable(conditionname) ) {
153 alert(conditionname + " not in " + eventtable + "; disabling");
155 cond_select.parentNode.parentNode.style.display = 'none';
156 cond_select.disabled = 'disabled';
159 alert(conditionname + " implicit for " + eventtable + "; enabling");
161 cond_select.parentNode.parentNode.style.display = '';
162 cond_select.disabled = '';
171 // update action select
174 // save off the current value first!!
175 var action = what.form.action.options[what.form.action.selectedIndex].value;
176 var a_desc = what.form.action.options[what.form.action.selectedIndex].text;
177 var seen_action = false;
179 // blank the current action select
180 for ( var i = what.form.action.length; i >= 0; i-- )
181 what.form.action.options[i] = null;
183 if ( action == '' ) {
184 opt(what.form.action, action, a_desc, true );
188 % foreach my $eventtable ( FS::part_event->eventtables ) {
189 % tie my %actions, 'Tie::IxHash', FS::part_event->actions($eventtable);
190 % #use Data::Dumper; warn Dumper(%actions);
192 if ( eventtable == '<% $eventtable %>' ) {
194 % foreach my $action ( keys %actions ) {
195 % ( my $description = $actions{$action}->{'description'} ) =~ s/'/\\'/g;
198 if ( action == '<% $action %>' ) {
202 opt( what.form.action, '<% $action %>', '<% $description %>', sel );
209 // by default, turn off warnings and enable the submit button
210 var warning = document.getElementById('action_warning');
211 warning.style.display = 'none';
212 var submit_button = document.getElementById('submit');
213 submit_button.disabled = '';
215 if ( ! seen_action && action != '' ) {
216 // add the current (not valid) action back
217 opt( what.form.action, action, a_desc, true );
218 // turn on a warning and disable the submit button
219 //warning.innerHTML = a_desc + ' event not available as a ' +
220 warning.innerHTML = 'Not available as a ' + eventdesc + ' action';
221 warning.style.display = '';
222 submit_button.disabled = 'disabled';
227 function opt(what,value,text,selected) {
228 var optionName = new Option(text, value, false, selected);
229 var length = what.length;
230 what.options[length] = optionName;
233 function action_changed(what) {
234 // remove '** Select new **'
235 if ( what.options[0].value == '' ) {
236 what.options[0] = null;
238 // remove the warning, remove the invalid action, enable the submit button
239 var warning = document.getElementById('action_warning');
240 if ( warning.style.display == '' ) {
241 warning.style.display = 'none';
242 what.options[what.length-1] = null;
243 document.getElementById('submit').disabled = '';
247 function condition_changed(what) {
248 // remove '** Select new **'
249 if ( what.options[0].value == '' ) {
250 what.options[0] = null;
253 var previousValue = what.getAttribute('previousValue');
254 var previousText = what.getAttribute('previousText');
255 var value = what.options[what.selectedIndex].value;
256 var text = what.options[what.selectedIndex].text;
258 % foreach my $value ( keys %condition_remove_warnings ) {
259 if ( previousValue == '<% $value %>' ) {
260 if ( !confirm( <% $condition_remove_warnings{$value} |js_string %> ) ) {
261 for ( var i=0; i < what.length; i++ ) {
262 if ( what.options[i].value == previousValue ) {
263 what.selectedIndex = i;
271 //alert(previous + ' changed to ' + value);
273 var field_regex = /(\d+)$/;
274 var match = field_regex.exec(what.name);
276 alert(what.name + " didn't match?!");
280 //add the previous condition *back* to all the other selects...
281 condition_add(previousValue, previousText, match[1]);
283 what.setAttribute('previousValue', value);
284 what.setAttribute('previousText', text);
286 // remove the new condition from all other selects
287 condition_remove(value, match[1]);
291 function condition_avail(check_cond, curnum) {
292 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
293 if ( cnum == curnum ) continue;
295 var cond_id = 'conditionname' + cnum;
296 var cond_select = document.getElementById(cond_id);
298 //alert("checking " + cond_id + " (" + cond_select.disabled + ")");
300 if ( cond_select.disabled ) continue;
303 var conditionname = cond_select.options[cond_select.selectedIndex].value;
305 if ( check_cond == conditionname ) return false;
313 function condition_remove(remove_cond, curnum) {
315 if ( remove_cond.length == 0 ) return;
317 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
318 if ( cnum == curnum ) continue;
320 var cond_id = 'conditionname' + cnum;
321 var cond_select = document.getElementById(cond_id);
323 //for ( var i = cond_select.length; i >= 0; i-- ) {
324 for ( var i=0; i < cond_select.length; i++ ) {
325 if ( cond_select.options[i].value == remove_cond ) {
326 cond_select.options[i] = null;
334 function condition_add(add_condname, add_conddesc, curnum) {
336 if ( add_condname.length == 0 ) return;
338 var in_eventtable = condition_in_eventtable(add_condname);
340 if ( ! in_eventtable ) return;
342 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
343 if ( cnum == curnum ) continue;
345 var cond_id = 'conditionname' + cnum;
346 var cond_select = document.getElementById(cond_id);
348 if ( cond_select.disabled ) continue;
350 //alert("adding " + add_condname + " to " + cond_id);
352 opt(cond_select, add_condname, add_conddesc, false );
354 cond_select.parentNode.parentNode.style.display = '';
360 function condition_in_eventtable(condname) {
362 var eventtable_el = document.getElementById('eventtable');
363 var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
365 var in_eventtable = false;
367 % foreach my $eventtable ( FS::part_event->eventtables ) {
368 % tie my %conditions, 'Tie::IxHash',
369 % FS::part_event_condition->conditions($eventtable);
371 if ( eventtable == '<% $eventtable %>' ) {
373 % foreach my $conditionname ( keys %conditions ) {
375 if ( condname == '<% $conditionname %>' ) {
376 in_eventtable = true;
385 return in_eventtable;
389 function condition_is_implicit(condname) {
391 if ( true <% @implicit_conditions
392 ? ( ' && '. join(' && ', map { "condname != '$_'" }
404 function condition_repop(cond_select) {
406 var eventtable_el = document.getElementById('eventtable');
407 var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
409 // save off the current value
410 var conditionname = cond_select.options[cond_select.selectedIndex].value;
411 var cond_desc = cond_select.options[cond_select.selectedIndex].text;
412 var seen_condition = false;
414 //skip deleted conditions
415 if ( cond_select.disabled && conditionname != '' && ! condition_is_implicit(conditionname) ) {
419 var field_regex = /(\d+)$/;
420 var match = field_regex.exec(cond_select.name);
422 alert(what.name + " didn't match?!");
427 // blank the current condition select
428 for ( var i = cond_select.length; i >= 0; i-- )
429 cond_select.options[i] = null;
431 if ( conditionname == '' ) {
432 opt(cond_select, conditionname, cond_desc, true );
436 % foreach my $eventtable ( FS::part_event->eventtables ) {
437 % tie my %conditions, 'Tie::IxHash',
438 % FS::part_event_condition->conditions($eventtable);
440 if ( eventtable == '<% $eventtable %>' ) {
442 % foreach my $conditionname ( keys %conditions ) {
443 % my $description = $conditions{$conditionname}->{'description'};
444 % $description =~ s/'/\\'/g;
447 if ( conditionname == '<% $conditionname %>' ) {
448 seen_condition = true;
452 if ( condition_avail("<% $conditionname %>", cnum) ) {
453 opt(cond_select, '<% $conditionname %>', '<% $description %>', sel);
462 if ( cond_select.length > 1 || cond_select.length == 1 && cond_select.options[0].value.length > 0 ) {
464 cond_select.parentNode.parentNode.style.display = '';
465 cond_select.disabled = '';
468 cond_select.parentNode.parentNode.style.display = 'none';
469 cond_select.disabled = 'disabled';
472 return seen_condition;
476 function _initialize_changed(what) {
477 document.getElementById('disabled').disabled = what.checked;
478 if ( what.checked ) {
479 %# because it's not an immediately obvious concept
480 alert('Initializing the event will treat it as "already run" on the '+
481 'current date for all existing customers. This affects '+
482 'conditions that prevent running an event more than once.');
489 #misc (eventtable, check_freq)
491 my $eventtable_labels = FS::part_event->eventtable_labels;
492 $eventtable_labels->{''} = '** Select type **';
494 my $check_freq_labels = FS::part_event->check_freq_labels;
498 tie my %all_conditions, 'Tie::IxHash',
499 '' => { 'description' => '*** Select new condition ***', },
500 FS::part_event_condition->conditions();
502 # *** Select new condition *** sorts to the beginning anyway
503 (tied %all_conditions)->SortByValue;
505 my %condition_labels = map { $_ => $all_conditions{$_}->{'description'} }
506 keys %all_conditions;
508 #my %condition_fields = map { $_ => $all_conditions{$_}->{option_fields} }
509 # keys %all_conditions;
510 my %condition_fields = map { my $c = $_;
511 tie my %opts, 'Tie::IxHash',
512 @{ $all_conditions{$c}->{'option_fields'} || []};
513 %opts = ( map { ( "$c.$_" => $opts{$_} ); }
518 keys %all_conditions;
520 my @implicit_conditions = sort { $all_conditions{$a}->{'implicit_flag'} <=>
521 $all_conditions{$b}->{'implicit_flag'}
523 grep { $all_conditions{$_}->{'implicit_flag'} }
524 keys %all_conditions;
526 my @implicit_condition_objs = map {
527 new FS::part_event_condition {
528 'conditionname' => $_,
531 @implicit_conditions;
533 my %condition_remove_warnings =
534 map { ( $_ => $all_conditions{$_}->{'remove_warning'} ); }
535 grep { $all_conditions{$_}->{'remove_warning'} }
536 keys %all_conditions;
540 tie my %all_actions, 'Tie::IxHash',
541 '' => { 'description' => '*** Select event action ***', },
542 FS::part_event->actions();
544 (tied %all_actions)->SortByValue;
546 my %action_labels = map { $_ => $all_actions{$_}->{'description'} }
549 #my %action_fields = map { $_ => $all_actions{$_}->{option_fields} }
551 my %action_fields = map { my $action = $_;
552 tie my %opts, 'Tie::IxHash',
553 @{ $all_actions{$action}->{option_fields} || [] };
554 %opts = ( map { ( "$action.$_" => $opts{$_} ); }
557 ( $action => [ %opts ] );
568 qq( <FONT ID="${field}_warning" STYLE="display:none" COLOR="#FF0000">).
569 "Party Party Join us Join us".
574 my $action_layer_values = sub {
575 my( $cgi, $part_event ) = @_;
576 my $action = $cgi->param('action') || $part_event->action;
577 return {} unless $action;
578 scalar( #force hashref
580 #map { $_ => { $part_event->options } }
581 # keys %action_fields
582 map { my $action = $_;
583 my %fields = @{ $action_fields{$action} };
584 my %obj_opts = $part_event->options;
585 %obj_opts = map { ( "$action.$_" => $obj_opts{$_} ); }
588 map { #false laziness w/process/part_event.html
590 my $value = scalar($cgi->param($_)) || $obj_opts{$_};
592 if ( $option =~ /^(.*)\.reasonnum$/ && $value == -1 ) {
594 'typenum' => scalar( $cgi->param( "new${option}T" ) ),
595 'reason' => scalar( $cgi->param( "new${option}" ) ),
599 ( $option => $value );
603 ( $action => \%opts );
610 tie my %cgi_conditions, 'Tie::IxHash';
612 my $error_callback = sub {
613 my( $cgi, $object, $fields_listref ) = @_;
615 my @cond_params = grep /^conditionname\d+$/, $cgi->param;
617 %cgi_conditions = map {
619 my $conditionname = $cgi->param($param);
624 $cgi_key =~ /^$param\.$conditionname\.(.*)$/ or die 'wtf!';
626 #my $value = $cgi->param($_);
628 #my $info = $all_conditions->{$conditionname}
630 @{ $all_conditions{$conditionname}->{'option_fields'} || []};
631 my $info = $cond_opts{$key};
634 #false laziness w/process/part_event.html
635 if ( $info->{'type'} =~ /^(select|checkbox)-?multiple$/
636 or $info->{'type'} =~ /^select/ && $info->{'multiple'} ) {
637 $value = { map { $_ => 1 } $cgi->param($cgi_key) };
638 } elsif ( $info->{'type'} eq 'freq' ) {
639 $value = $cgi->param($cgi_key). $cgi->param($cgi_key.'_units');
641 $value = $cgi->param($cgi_key);
646 } grep /^$param\.$conditionname\./, $cgi->param
648 } grep $cgi->param($_), grep /^conditionname\d+$/, $cgi->param;
652 my $condition_error_callback = sub {
654 new FS::part_event_condition { 'conditionname' => $_, };
655 } keys %cgi_conditions;
658 my $condition_layer_values = sub {
659 #m2_table option causes this to be
660 # part_event_condition instead of part_event
661 my ( $cgi, $part_event_condition, $switches ) = @_;
662 scalar( #force hashref
664 #map { $_ => { $part_event_condition->options } }
665 # keys %condition_fields
666 map { my $conditionname = $_;
667 my %opts = $switches->{'mode'} eq 'error'
668 ? %{ $cgi_conditions{$conditionname} || {} }
669 : $part_event_condition->options;
671 map { ( "$conditionname.$_" => $opts{$_} ); }
674 ( $conditionname => \%opts );
676 keys %condition_fields
685 my $curuser = $FS::CurrentUser::CurrentUser;
688 unless $curuser->access_right('Edit billing events')
689 || $curuser->access_right('Edit global billing events');
691 my $disable_empty_agent= ! $curuser->access_right('Edit global billing events');
693 %cgi_conditions = ();
694 my $use_cgi_conditions = 0;