1 <% include( 'elements/edit.html',
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',
87 <SCRIPT TYPE="text/javascript">
89 window.onload = function () { eventtable_changed(document.getElementById('eventtable')) };
92 function eventtable_changed(what) {
95 alert('eventtable_changed called on ' + what );
98 var eventtable = what.options[what.selectedIndex].value;
100 alert ("eventtable: " + eventtable);
102 var eventdesc = what.options[what.selectedIndex].text;
104 //remove the ** Select type **
105 if ( what.options[0].value == '' && notonload++ > 0 ) {
106 what.options[0] = null;
110 // XXX gray out conditions that can't apply (in addition to the warning)?
114 // update condition selects
117 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
118 var cond_id = 'conditionname' + cnum;
119 var cond_select = document.getElementById(cond_id);
122 alert('updating ' + cond_id);
125 // save off the current value
126 var conditionname = cond_select.options[cond_select.selectedIndex].value;
127 var cond_desc = cond_select.options[cond_select.selectedIndex].text;
129 var seen_condition = condition_repop(cond_select);
131 var warning = document.getElementById(cond_id + '_warning');
133 alert('turning off warning; setting style.display of '+ cond_id +
134 '_warning (' + warning + ') to none');
136 warning.style.display = 'none';
138 if ( ! seen_condition && conditionname != '' ) {
139 // add the current (not valid) condition back
140 opt(cond_select, conditionname, cond_desc, true );
141 if ( ! condition_is_implicit(conditionname) ) {
142 cond_select.parentNode.parentNode.style.display = '';
143 cond_select.disabled = '';
144 // turn on a warning and gray out the condition row
146 alert('turning on warning; setting style.display of '+ cond_id +
147 '_warning (' + warning + ') to ""');
149 warning.innerHTML = 'Not applicable to ' + eventdesc + ' events';
150 warning.style.display = '';
152 if ( ! condition_in_eventtable(conditionname) ) {
154 alert(conditionname + " not in " + eventtable + "; disabling");
156 cond_select.parentNode.parentNode.style.display = 'none';
157 cond_select.disabled = 'disabled';
160 alert(conditionname + " implicit for " + eventtable + "; enabling");
162 cond_select.parentNode.parentNode.style.display = '';
163 cond_select.disabled = '';
172 // update action select
175 // save off the current value first!!
176 var action = what.form.action.options[what.form.action.selectedIndex].value;
177 var a_desc = what.form.action.options[what.form.action.selectedIndex].text;
178 var seen_action = false;
180 // blank the current action select
181 for ( var i = what.form.action.length; i >= 0; i-- )
182 what.form.action.options[i] = null;
184 if ( action == '' ) {
185 opt(what.form.action, action, a_desc, true );
189 % foreach my $eventtable ( FS::part_event->eventtables ) {
190 % tie my %actions, 'Tie::IxHash', FS::part_event->actions($eventtable);
191 % #use Data::Dumper; warn Dumper(%actions);
193 if ( eventtable == '<% $eventtable %>' ) {
195 % foreach my $action ( keys %actions ) {
196 % ( my $description = $actions{$action}->{'description'} ) =~ s/'/\\'/g;
199 if ( action == '<% $action %>' ) {
203 opt( what.form.action, '<% $action %>', '<% $description %>', sel );
210 // by default, turn off warnings and enable the submit button
211 var warning = document.getElementById('action_warning');
212 warning.style.display = 'none';
213 var submit_button = document.getElementById('submit');
214 submit_button.disabled = '';
216 if ( ! seen_action && action != '' ) {
217 // add the current (not valid) action back
218 opt( what.form.action, action, a_desc, true );
219 // turn on a warning and disable the submit button
220 //warning.innerHTML = a_desc + ' event not available as a ' +
221 warning.innerHTML = 'Not available as a ' + eventdesc + ' action';
222 warning.style.display = '';
223 submit_button.disabled = 'disabled';
228 function opt(what,value,text,selected) {
229 var optionName = new Option(text, value, false, selected);
230 var length = what.length;
231 what.options[length] = optionName;
234 function action_changed(what) {
235 // remove '** Select new **'
236 if ( what.options[0].value == '' ) {
237 what.options[0] = null;
239 // remove the warning, remove the invalid action, enable the submit button
240 var warning = document.getElementById('action_warning');
241 if ( warning.style.display == '' ) {
242 warning.style.display = 'none';
243 what.options[what.length-1] = null;
244 document.getElementById('submit').disabled = '';
248 function condition_changed(what) {
249 // remove '** Select new **'
250 if ( what.options[0].value == '' ) {
251 what.options[0] = null;
254 var previousValue = what.getAttribute('previousValue');
255 var previousText = what.getAttribute('previousText');
256 var value = what.options[what.selectedIndex].value;
257 var text = what.options[what.selectedIndex].text;
259 % foreach my $value ( keys %condition_remove_warnings ) {
260 if ( previousValue == '<% $value %>' ) {
261 if ( !confirm( <% $condition_remove_warnings{$value} |js_string %> ) ) {
262 for ( var i=0; i < what.length; i++ ) {
263 if ( what.options[i].value == previousValue ) {
264 what.selectedIndex = i;
272 //alert(previous + ' changed to ' + value);
274 var field_regex = /(\d+)$/;
275 var match = field_regex.exec(what.name);
277 alert(what.name + " didn't match?!");
281 //add the previous condition *back* to all the other selects...
282 condition_add(previousValue, previousText, match[1]);
284 what.setAttribute('previousValue', value);
285 what.setAttribute('previousText', text);
287 // remove the new condition from all other selects
288 condition_remove(value, match[1]);
292 function condition_avail(check_cond, curnum) {
293 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
294 if ( cnum == curnum ) continue;
296 var cond_id = 'conditionname' + cnum;
297 var cond_select = document.getElementById(cond_id);
299 //alert("checking " + cond_id + " (" + cond_select.disabled + ")");
301 if ( cond_select.disabled ) continue;
304 var conditionname = cond_select.options[cond_select.selectedIndex].value;
306 if ( check_cond == conditionname ) return false;
314 function condition_remove(remove_cond, curnum) {
316 if ( remove_cond.length == 0 ) return;
318 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
319 if ( cnum == curnum ) continue;
321 var cond_id = 'conditionname' + cnum;
322 var cond_select = document.getElementById(cond_id);
324 //for ( var i = cond_select.length; i >= 0; i-- ) {
325 for ( var i=0; i < cond_select.length; i++ ) {
326 if ( cond_select.options[i].value == remove_cond ) {
327 cond_select.options[i] = null;
335 function condition_add(add_condname, add_conddesc, curnum) {
337 if ( add_condname.length == 0 ) return;
339 var in_eventtable = condition_in_eventtable(add_condname);
341 if ( ! in_eventtable ) return;
343 for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
344 if ( cnum == curnum ) continue;
346 var cond_id = 'conditionname' + cnum;
347 var cond_select = document.getElementById(cond_id);
349 if ( cond_select.disabled ) continue;
351 //alert("adding " + add_condname + " to " + cond_id);
353 opt(cond_select, add_condname, add_conddesc, false );
355 cond_select.parentNode.parentNode.style.display = '';
361 function condition_in_eventtable(condname) {
363 var eventtable_el = document.getElementById('eventtable');
364 var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
366 var in_eventtable = false;
368 % foreach my $eventtable ( FS::part_event->eventtables ) {
369 % tie my %conditions, 'Tie::IxHash',
370 % FS::part_event_condition->conditions($eventtable);
372 if ( eventtable == '<% $eventtable %>' ) {
374 % foreach my $conditionname ( keys %conditions ) {
376 if ( condname == '<% $conditionname %>' ) {
377 in_eventtable = true;
386 return in_eventtable;
390 function condition_is_implicit(condname) {
392 if ( true <% @implicit_conditions
393 ? ( ' && '. join(' && ', map { "condname != '$_'" }
405 function condition_repop(cond_select) {
407 var eventtable_el = document.getElementById('eventtable');
408 var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
410 // save off the current value
411 var conditionname = cond_select.options[cond_select.selectedIndex].value;
412 var cond_desc = cond_select.options[cond_select.selectedIndex].text;
413 var seen_condition = false;
415 //skip deleted conditions
416 if ( cond_select.disabled && conditionname != '' && ! condition_is_implicit(conditionname) ) {
420 var field_regex = /(\d+)$/;
421 var match = field_regex.exec(cond_select.name);
423 alert(what.name + " didn't match?!");
428 // blank the current condition select
429 for ( var i = cond_select.length; i >= 0; i-- )
430 cond_select.options[i] = null;
432 if ( conditionname == '' ) {
433 opt(cond_select, conditionname, cond_desc, true );
437 % foreach my $eventtable ( FS::part_event->eventtables ) {
438 % tie my %conditions, 'Tie::IxHash',
439 % FS::part_event_condition->conditions($eventtable);
441 if ( eventtable == '<% $eventtable %>' ) {
443 % foreach my $conditionname ( keys %conditions ) {
444 % my $description = $conditions{$conditionname}->{'description'};
445 % $description =~ s/'/\\'/g;
448 if ( conditionname == '<% $conditionname %>' ) {
449 seen_condition = true;
453 if ( condition_avail("<% $conditionname %>", cnum) ) {
454 opt(cond_select, '<% $conditionname %>', '<% $description %>', sel);
463 if ( cond_select.length > 1 || cond_select.length == 1 && cond_select.options[0].value.length > 0 ) {
465 cond_select.parentNode.parentNode.style.display = '';
466 cond_select.disabled = '';
469 cond_select.parentNode.parentNode.style.display = 'none';
470 cond_select.disabled = 'disabled';
473 return seen_condition;
477 function _initialize_changed(what) {
478 document.getElementById('disabled').disabled = what.checked;
479 if ( what.checked ) {
480 %# because it's not an immediately obvious concept
481 alert('Initializing the event will treat it as "already run" on the '+
482 'current date for all existing customers. This affects '+
483 'conditions that prevent running an event more than once.');
490 #misc (eventtable, check_freq)
492 my $eventtable_labels = FS::part_event->eventtable_labels;
493 $eventtable_labels->{''} = '** Select type **';
495 my $check_freq_labels = FS::part_event->check_freq_labels;
499 tie my %all_conditions, 'Tie::IxHash',
500 '' => { 'description' => '*** Select new condition ***', },
501 FS::part_event_condition->conditions();
503 # *** Select new condition *** sorts to the beginning anyway
504 (tied %all_conditions)->SortByValue;
506 my %condition_labels = map { $_ => $all_conditions{$_}->{'description'} }
507 keys %all_conditions;
509 #my %condition_fields = map { $_ => $all_conditions{$_}->{option_fields} }
510 # keys %all_conditions;
511 my %condition_fields = map { my $c = $_;
512 tie my %opts, 'Tie::IxHash',
513 @{ $all_conditions{$c}->{'option_fields'} || []};
514 %opts = ( map { ( "$c.$_" => $opts{$_} ); }
519 keys %all_conditions;
521 my @implicit_conditions = sort { $all_conditions{$a}->{'implicit_flag'} <=>
522 $all_conditions{$b}->{'implicit_flag'}
524 grep { $all_conditions{$_}->{'implicit_flag'} }
525 keys %all_conditions;
527 my @implicit_condition_objs = map {
528 new FS::part_event_condition {
529 'conditionname' => $_,
532 @implicit_conditions;
534 my %condition_remove_warnings =
535 map { ( $_ => $all_conditions{$_}->{'remove_warning'} ); }
536 grep { $all_conditions{$_}->{'remove_warning'} }
537 keys %all_conditions;
541 tie my %all_actions, 'Tie::IxHash',
542 '' => { 'description' => '*** Select event action ***', },
543 FS::part_event->actions();
545 (tied %all_actions)->SortByValue;
547 my %action_labels = map { $_ => $all_actions{$_}->{'description'} }
550 #my %action_fields = map { $_ => $all_actions{$_}->{option_fields} }
552 my %action_fields = map { my $action = $_;
553 tie my %opts, 'Tie::IxHash',
554 @{ $all_actions{$action}->{option_fields} || [] };
555 %opts = ( map { ( "$action.$_" => $opts{$_} ); }
558 ( $action => [ %opts ] );
569 qq( <FONT ID="${field}_warning" STYLE="display:none" COLOR="#FF0000">).
570 "Party Party Join us Join us".
575 my $action_layer_values = sub {
576 my( $cgi, $part_event ) = @_;
577 my $action = $cgi->param('action') || $part_event->action;
578 return {} unless $action;
579 scalar( #force hashref
581 #map { $_ => { $part_event->options } }
582 # keys %action_fields
583 map { my $action = $_;
584 my %fields = @{ $action_fields{$action} };
585 my %obj_opts = $part_event->options;
586 %obj_opts = map { ( "$action.$_" => $obj_opts{$_} ); }
589 map { #false laziness w/process/part_event.html
591 my $value = scalar($cgi->param($_)) || $obj_opts{$_};
593 if ( $option =~ /^(.*)\.reasonnum$/ && $value == -1 ) {
595 'typenum' => scalar( $cgi->param( "new${option}T" ) ),
596 'reason' => scalar( $cgi->param( "new${option}" ) ),
600 ( $option => $value );
604 ( $action => \%opts );
611 tie my %cgi_conditions, 'Tie::IxHash';
613 my $error_callback = sub {
614 my( $cgi, $object, $fields_listref ) = @_;
616 my @cond_params = grep /^conditionname\d+$/, $cgi->param;
618 %cgi_conditions = map {
620 my $conditionname = $cgi->param($param);
625 $cgi_key =~ /^$param\.$conditionname\.(.*)$/ or die 'wtf!';
627 #my $value = $cgi->param($_);
629 #my $info = $all_conditions->{$conditionname}
631 @{ $all_conditions{$conditionname}->{'option_fields'} || []};
632 my $info = $cond_opts{$key};
635 #false laziness w/process/part_event.html
636 if ( $info->{'type'} =~ /^(select|checkbox)-?multiple$/
637 or $info->{'type'} =~ /^select/ && $info->{'multiple'} ) {
638 $value = { map { $_ => 1 } $cgi->param($cgi_key) };
639 } elsif ( $info->{'type'} eq 'freq' ) {
640 $value = $cgi->param($cgi_key). $cgi->param($cgi_key.'_units');
642 $value = $cgi->param($cgi_key);
647 } grep /^$param\.$conditionname\./, $cgi->param
649 } grep $cgi->param($_), grep /^conditionname\d+$/, $cgi->param;
653 my $condition_error_callback = sub {
655 new FS::part_event_condition { 'conditionname' => $_, };
656 } keys %cgi_conditions;
659 my $condition_layer_values = sub {
660 #m2_table option causes this to be
661 # part_event_condition instead of part_event
662 my ( $cgi, $part_event_condition, $switches ) = @_;
663 scalar( #force hashref
665 #map { $_ => { $part_event_condition->options } }
666 # keys %condition_fields
667 map { my $conditionname = $_;
668 my %opts = $switches->{'mode'} eq 'error'
669 ? %{ $cgi_conditions{$conditionname} || {} }
670 : $part_event_condition->options;
672 map { ( "$conditionname.$_" => $opts{$_} ); }
675 ( $conditionname => \%opts );
677 keys %condition_fields
686 my $curuser = $FS::CurrentUser::CurrentUser;
689 unless $curuser->access_right('Edit billing events')
690 || $curuser->access_right('Edit global billing events');
692 my $disable_empty_agent= ! $curuser->access_right('Edit global billing events');
694 %cgi_conditions = ();
695 my $use_cgi_conditions = 0;