communigate (phase 2): rules. RT#7514
[freeside.git] / httemplate / edit / part_event.html
1 <% include( 'elements/edit.html',
2               'name'   => 'Billing event definition',
3               'table'  => 'part_event',
4               'fields' => [
5                             'event',
6                             { field   => 'eventtable',
7                               type    => 'select',
8                               options => [ FS::part_event->eventtables ],
9                               labels  => $eventtable_labels,
10                               onchange => 'eventtable_changed',
11                             },
12                             { field   => 'agentnum',
13                               type    => 'select-agent',
14                               disable_empty => $disable_empty_agent,
15                             },
16                             { field   => 'check_freq',
17                               type    => 'select',
18                               options => [ '1d', '1m' ],
19                               labels  => $check_freq_labels,
20                             },
21                             { field   => 'disabled',
22                               type    => 'checkbox',
23                               value   => 'Y',
24                             },
25                             { type    => 'title',
26                               value   => 'Event Conditions',
27                             },
28                             { field   => 'conditionname',
29                               type    => 'selectlayers',
30                               options => [ keys %all_conditions ],
31                               labels  => \%condition_labels,
32                               onchange => 'condition_changed(what);',
33                               layer_fields => \%condition_fields,
34                               layer_values_callback => $condition_layer_values,
35                               html_between       => n_a('action'),
36                               m2name_table       => 'part_event_condition',
37                               m2name_namecol     => 'conditionname',
38                               m2_label           => 'Condition',
39                               m2_new_default     => \@implicit_condition_objs,
40                               m2_error_callback  => $condition_error_callback,
41                               m2_remove_warnings => \%condition_remove_warnings,
42                               m2_new_js          => 'condition_repop',
43                               m2_remove_js       => 'condition_add',
44                             },
45                             { type    => 'title',
46                               value   => 'Event Action',
47                             },
48                             { field   => 'action',
49                               type     => 'selectlayers',
50                               options  => [ keys %all_actions ],
51                               labels   => \%action_labels,
52                               onchange => 'action_changed(what);',
53                               layer_fields => \%action_fields,
54                               layer_values_callback => $action_layer_values,
55                               html_between => n_a('action'),
56                             },
57
58                           ],
59               'labels' => {
60                             'eventpart'  => 'Event',
61                             'event'      => 'Event name',
62                             'eventtable' => 'Type',
63                             'agentnum'   => 'Agent',
64                             'check_freq' => 'Check frequency',
65                             'disabled'   => 'Disable event',
66
67                             'conditionname' => 'Add&nbsp;new&nbsp;condition',
68                             #'weight',
69                             'action'     => 'Action',
70                           },
71               'viewall_dir' => 'browse',
72               'new_callback' => sub { #start empty for new events only
73                 my( $cgi, $object, $fields_listref ) = @_; 
74                 unshift @{ $fields_listref->[1]{'options'} }, '';
75               },
76               'error_callback' => $error_callback,
77
78               'agent_virt'       => 1,
79               'agent_null_right' => 'Edit global billing events',
80           )
81 %>
82 <SCRIPT TYPE="text/javascript">
83
84   window.onload = function () { eventtable_changed(document.getElementById('eventtable')) };
85   var notonload = 0;
86
87   function eventtable_changed(what) {
88
89 %   if ( $JS_DEBUG ) {
90       alert('eventtable_changed called on ' + what );
91 %   }
92
93     var eventtable = what.options[what.selectedIndex].value;
94 %   if ( $JS_DEBUG ) {
95       alert ("eventtable: " + eventtable);
96 %   }
97     var eventdesc  = what.options[what.selectedIndex].text;
98
99     //remove the ** Select type **
100     if ( what.options[0].value == '' && notonload++ > 0 ) {
101       what.options[0] = null;
102     }
103
104     ////
105     // XXX gray out conditions that can't apply (in addition to the warning)?
106     ////
107
108     ////
109     // update condition selects
110     ////
111
112     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
113       var cond_id = 'conditionname' + cnum;
114       var cond_select = document.getElementById(cond_id);
115
116 %     if ( $JS_DEBUG ) {
117         alert('updating ' + cond_id);
118 %     }
119
120       // save off the current value
121       var conditionname = cond_select.options[cond_select.selectedIndex].value;
122       var cond_desc     = cond_select.options[cond_select.selectedIndex].text;
123
124       var seen_condition = condition_repop(cond_select);
125
126       var warning = document.getElementById(cond_id + '_warning');
127 %     if ( $JS_DEBUG ) {
128         alert('turning off warning; setting style.display of '+ cond_id +
129               '_warning (' + warning + ') to none');
130 %     }
131       warning.style.display = 'none';
132
133       if ( ! seen_condition && conditionname != '' ) {
134         // add the current (not valid) condition back
135         opt(cond_select, conditionname, cond_desc, true );
136         if ( ! condition_is_implicit(conditionname) ) {
137           cond_select.parentNode.parentNode.style.display = '';
138           cond_select.disabled = '';
139           // turn on a warning and gray out the condition row
140 %         if ( $JS_DEBUG ) {
141             alert('turning on warning; setting style.display of '+ cond_id +
142                   '_warning (' + warning + ') to ""');
143 %         }
144           warning.innerHTML = 'Not applicable to ' + eventdesc + ' events';
145           warning.style.display = '';
146         } else {
147           if ( ! condition_in_eventtable(conditionname) ) {
148 %           if ( $JS_DEBUG ) {
149               alert(conditionname + " not in " + eventtable + "; disabling");
150 %           }
151             cond_select.parentNode.parentNode.style.display = 'none';
152             cond_select.disabled = 'disabled';
153           } else {
154 %           if ( $JS_DEBUG ) {
155               alert(conditionname + " implicit for " + eventtable + "; enabling");
156 %           }
157             cond_select.parentNode.parentNode.style.display = '';
158             cond_select.disabled = '';
159           }
160         }
161       }
162
163     }
164
165
166     ////
167     // update action select
168     ////
169
170     // save off the current value first!!
171     var action = what.form.action.options[what.form.action.selectedIndex].value;
172     var a_desc = what.form.action.options[what.form.action.selectedIndex].text;
173     var seen_action = false;
174
175     // blank the current action select
176     for ( var i = what.form.action.length; i >= 0; i-- )
177       what.form.action.options[i] = null;
178
179     if ( action == '' ) {
180       opt(what.form.action, action, a_desc, true );
181     }
182
183     // repopulate it
184 %   foreach my $eventtable ( FS::part_event->eventtables ) {
185 %     tie my %actions, 'Tie::IxHash', FS::part_event->actions($eventtable);
186 %     #use Data::Dumper; warn Dumper(%actions);
187
188       if ( eventtable == '<% $eventtable %>' ) {
189
190 %       foreach my $action ( keys %actions ) {
191 %         ( my $description = $actions{$action}->{'description'} ) =~ s/'/\\'/g;
192
193           var sel = false;
194           if ( action == '<% $action %>' ) {
195             seen_action = true;
196             sel = true;
197           }
198           opt( what.form.action, '<% $action %>', '<% $description %>', sel );
199 %       }
200
201       }
202
203 %   }
204
205     // by default, turn off warnings and enable the submit button
206     var warning = document.getElementById('action_warning');
207     warning.style.display = 'none';
208     var submit_button = document.getElementById('submit');
209     submit_button.disabled = '';
210
211     if ( ! seen_action && action != '' ) {
212       // add the current (not valid) action back
213       opt( what.form.action, action, a_desc, true );
214       // turn on a warning and disable the submit button
215       //warning.innerHTML = a_desc + ' event not available as a ' +
216       warning.innerHTML = 'Not available as a ' + eventdesc + ' action';
217       warning.style.display = '';
218       submit_button.disabled = 'disabled';
219     }
220
221   }
222
223   function opt(what,value,text,selected) {
224     var optionName = new Option(text, value, false, selected);
225     var length = what.length;
226     what.options[length] = optionName;
227   }
228
229   function action_changed(what) {
230     // remove '** Select new **'
231     if ( what.options[0].value == '' ) {
232        what.options[0] = null;
233     }
234     // remove the warning, remove the invalid action, enable the submit button
235     var warning = document.getElementById('action_warning');
236     if ( warning.style.display == '' ) {
237       warning.style.display = 'none';
238       what.options[what.length-1] = null;
239       document.getElementById('submit').disabled = '';
240     }
241   }
242
243   function condition_changed(what) {
244     // remove '** Select new **'
245     if ( what.options[0].value == '' ) {
246        what.options[0] = null;
247     }
248
249     var previousValue = what.getAttribute('previousValue');
250     var previousText = what.getAttribute('previousText');
251     var value = what.options[what.selectedIndex].value;
252     var text = what.options[what.selectedIndex].text;
253
254 %   foreach my $value ( keys %condition_remove_warnings ) {
255       if ( previousValue == '<% $value %>' ) {
256         if ( !confirm( <% $condition_remove_warnings{$value} |js_string %> ) ) {
257           for ( var i=0; i < what.length; i++ ) {
258             if ( what.options[i].value == previousValue ) {
259               what.selectedIndex = i;
260             }
261           }
262           return false;
263         }
264       }
265 %   }
266
267     //alert(previous + ' changed to ' + value);
268
269     var field_regex = /(\d+)$/;
270     var match = field_regex.exec(what.name);
271     if ( !match ) {
272       alert(what.name + " didn't match?!");
273       return;
274     }
275
276     //add the previous condition *back* to all the other selects...
277     condition_add(previousValue, previousText, match[1]);
278
279     what.setAttribute('previousValue', value);
280     what.setAttribute('previousText', text);
281
282     // remove the new condition from all other selects
283     condition_remove(value, match[1]);
284
285   }
286
287   function condition_avail(check_cond, curnum) {
288     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
289       if ( cnum == curnum ) continue;
290
291       var cond_id = 'conditionname' + cnum;
292       var cond_select = document.getElementById(cond_id);
293
294       //alert("checking " + cond_id + " (" + cond_select.disabled + ")");
295
296       if ( cond_select.disabled ) continue;
297
298       // the current value
299       var conditionname = cond_select.options[cond_select.selectedIndex].value;
300
301       if ( check_cond == conditionname ) return false;
302
303     }
304
305     return true;
306
307   }
308
309   function condition_remove(remove_cond, curnum) {
310
311     if ( remove_cond.length == 0 ) return;
312
313     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
314       if ( cnum == curnum ) continue;
315
316       var cond_id = 'conditionname' + cnum;
317       var cond_select = document.getElementById(cond_id);
318
319       //for ( var i = cond_select.length; i >= 0; i-- ) {
320       for ( var i=0; i < cond_select.length; i++ ) {
321         if ( cond_select.options[i].value == remove_cond ) {
322           cond_select.options[i] = null;
323         }
324       }
325
326     }
327
328   }
329
330   function condition_add(add_condname, add_conddesc, curnum) {
331
332     if ( add_condname.length == 0 ) return;
333
334     var in_eventtable = condition_in_eventtable(add_condname);
335
336     if ( ! in_eventtable ) return;
337
338     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
339       if ( cnum == curnum ) continue;
340
341       var cond_id = 'conditionname' + cnum;
342       var cond_select = document.getElementById(cond_id);
343
344       if ( cond_select.disabled ) continue;
345
346       //alert("adding " + add_condname + " to " + cond_id);
347
348       opt(cond_select, add_condname, add_conddesc, false );
349
350       cond_select.parentNode.parentNode.style.display = '';
351
352     }
353
354   }
355
356   function condition_in_eventtable(condname) {
357
358     var eventtable_el = document.getElementById('eventtable');
359     var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
360
361     var in_eventtable = false;
362
363 %   foreach my $eventtable ( FS::part_event->eventtables ) {
364 %     tie my %conditions, 'Tie::IxHash',
365 %       FS::part_event_condition->conditions($eventtable);
366
367       if ( eventtable == '<% $eventtable %>' ) {
368
369 %       foreach my $conditionname ( keys %conditions ) {
370
371           if ( condname == '<% $conditionname %>' ) {
372             in_eventtable = true;
373           }
374
375 %       }
376
377       }
378
379 %   }
380
381     return in_eventtable;
382
383   }
384
385   function condition_is_implicit(condname) {
386
387     if ( true <% @implicit_conditions
388                    ? ( ' && '. join(' && ', map { "condname != '$_'" }
389                                                 @implicit_conditions
390                                    )
391                      )
392                    : ''
393               %> ) {
394       return false;
395     } else {
396       return true;
397     }
398   }
399
400   function condition_repop(cond_select) {
401
402     var eventtable_el = document.getElementById('eventtable');
403     var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
404
405     // save off the current value
406     var conditionname = cond_select.options[cond_select.selectedIndex].value;
407     var cond_desc     = cond_select.options[cond_select.selectedIndex].text;
408     var seen_condition = false;
409
410     //skip deleted conditions
411     if ( cond_select.disabled && conditionname != '' && ! condition_is_implicit(conditionname) ) {
412       return false;
413     }
414
415     var field_regex = /(\d+)$/;
416     var match = field_regex.exec(cond_select.name);
417     if ( !match ) {
418       alert(what.name + " didn't match?!");
419       return;
420     }
421     var cnum = match[1];
422
423     // blank the current condition select
424     for ( var i = cond_select.length; i >= 0; i-- )
425       cond_select.options[i] = null;
426
427     if ( conditionname == '' ) {
428       opt(cond_select, conditionname, cond_desc, true );
429     }
430
431     // repopulate it
432 %   foreach my $eventtable ( FS::part_event->eventtables ) {
433 %     tie my %conditions, 'Tie::IxHash',
434 %       FS::part_event_condition->conditions($eventtable);
435
436       if ( eventtable == '<% $eventtable %>' ) {
437
438 %       foreach my $conditionname ( keys %conditions ) {
439 %         my $description = $conditions{$conditionname}->{'description'};
440 %         $description =~ s/'/\\'/g;
441
442           var sel = false;
443           if ( conditionname == '<% $conditionname %>' ) {
444             seen_condition = true;
445             sel = true;
446           }
447
448           if ( condition_avail("<% $conditionname %>", cnum) ) {
449             opt(cond_select, '<% $conditionname %>', '<% $description %>', sel);
450           }
451
452 %       }
453
454       }
455         
456 %   }
457
458     if ( cond_select.length > 1 || cond_select.length == 1 && cond_select.options[0].value.length > 0 ) {
459        
460       cond_select.parentNode.parentNode.style.display = '';
461       cond_select.disabled = '';
462
463     } else {
464       cond_select.parentNode.parentNode.style.display = 'none';
465       cond_select.disabled = 'disabled';
466     }
467
468     return seen_condition;
469
470   }
471
472 </SCRIPT>
473 <%once>
474
475 #misc (eventtable, check_freq)
476
477 my $eventtable_labels = FS::part_event->eventtable_labels;
478 $eventtable_labels->{''} = '** Select type **';
479
480 my $check_freq_labels = FS::part_event->check_freq_labels;
481
482 #conditions
483
484 tie my %all_conditions, 'Tie::IxHash', 
485   '' => { 'description' => '*** Select new condition ***', },
486   FS::part_event_condition->conditions();
487
488 my %condition_labels = map { $_ => $all_conditions{$_}->{'description'} }
489                            keys %all_conditions;
490
491 #my %condition_fields = map { $_ => $all_conditions{$_}->{option_fields} } 
492 #                           keys %all_conditions;
493 my %condition_fields = map { my $c = $_;
494                              tie my %opts, 'Tie::IxHash',
495                                @{ $all_conditions{$c}->{'option_fields'} || []};
496                              %opts = ( map { ( "$c.$_" => $opts{$_} ); }
497                                            keys %opts
498                                      );
499                              ( $c => [ %opts ] );
500                            } 
501                            keys %all_conditions;
502
503 my @implicit_conditions = sort { $all_conditions{$a}->{'implicit_flag'} <=>
504                                  $all_conditions{$b}->{'implicit_flag'}
505                                }
506                           grep { $all_conditions{$_}->{'implicit_flag'} }
507                           keys %all_conditions;
508
509 my @implicit_condition_objs = map {
510                                     new FS::part_event_condition {
511                                       'conditionname' => $_,
512                                     };
513                                   }
514                                   @implicit_conditions;
515
516 my %condition_remove_warnings =
517   map  { ( $_ => $all_conditions{$_}->{'remove_warning'} ); }
518   grep { $all_conditions{$_}->{'remove_warning'} }
519   keys %all_conditions;
520
521 #actions
522
523 tie my %all_actions, 'Tie::IxHash', 
524   '' => { 'description' => '*** Select event action ***', },
525   FS::part_event->actions();
526
527 my %action_labels = map { $_ => $all_actions{$_}->{'description'} }
528                         keys %all_actions;
529
530 #my %action_fields = map { $_ => $all_actions{$_}->{option_fields} }
531 #                        keys %all_actions;
532 my %action_fields = map { my $action = $_;
533                           tie my %opts, 'Tie::IxHash',
534                             @{ $all_actions{$action}->{option_fields} || [] };
535                           %opts = ( map { ( "$action.$_" => $opts{$_} ); }
536                                         keys %opts
537                                   );
538                           ( $action => [ %opts ] );
539                         }
540                         keys %all_actions;
541
542 #subs
543
544 sub n_a {
545   my $t = shift;
546
547   return sub {
548     my $field = shift;
549     qq( <FONT ID="${field}_warning" STYLE="display:none" COLOR="#FF0000">).
550       "Party Party Join us Join us".
551       '</FONT>';
552   };
553 }
554
555 my $action_layer_values = sub {
556   my( $cgi, $part_event ) = @_;
557   my $action = $cgi->param('action') || $part_event->action;
558   return {} unless $action;
559   scalar( #force hashref
560     {
561       #map { $_ => { $part_event->options } }
562       #    keys %action_fields
563       map { my $action = $_;
564             my %fields = @{ $action_fields{$action} };
565             my %obj_opts = $part_event->options;
566             %obj_opts = map { ( "$action.$_" => $obj_opts{$_} ); }
567                             keys %obj_opts;
568             my %opts =
569               map { #false laziness w/process/part_event.html
570                     my $option = $_;
571                     my $value = scalar($cgi->param($_)) || $obj_opts{$_};
572
573                     if ( $option =~ /^(.*)\.reasonnum$/ && $value == -1 ) {
574                       $value = {
575                         'typenum' => scalar( $cgi->param( "new${option}T" ) ),
576                         'reason'  => scalar( $cgi->param( "new${option}"  ) ),
577                       };
578                     }
579
580                     ( $option => $value );
581
582                   }
583                   keys %fields;
584             ( $action => \%opts );
585           }
586           keys %action_fields
587     }
588   );
589 };
590
591 tie my %cgi_conditions, 'Tie::IxHash';
592
593 my $error_callback = sub {
594   my( $cgi, $object, $fields_listref ) = @_;
595
596   my @cond_params = grep /^conditionname\d+$/, $cgi->param;
597
598   %cgi_conditions = map {
599     my $param = $_;
600     my $conditionname = $cgi->param($param);
601     $conditionname => {
602       map { 
603
604         my $cgi_key = $_;
605         $cgi_key =~ /^$param\.$conditionname\.(.*)$/ or die 'wtf!';
606         my $key = $1;
607         #my $value = $cgi->param($_);
608
609         #my $info = $all_conditions->{$conditionname}
610         my %cond_opts =
611           @{ $all_conditions{$conditionname}->{'option_fields'} || []};
612         my $info = $cond_opts{$key};
613
614         my $value;
615         #false laziness w/process/part_event.html
616         if (      $info->{'type'} =~ /^(select|checkbox)-?multiple$/
617                or $info->{'type'} =~ /^select/ && $info->{'multiple'} ) {
618           $value = { map { $_ => 1 } $cgi->param($cgi_key) };
619         } elsif ( $info->{'type'} eq 'freq' ) {
620           $value = $cgi->param($cgi_key). $cgi->param($cgi_key.'_units');
621         } else {
622           $value = $cgi->param($cgi_key);
623         }
624
625         $key => $value;
626
627       } grep /^$param\.$conditionname\./, $cgi->param
628     };
629   } grep $cgi->param($_), grep /^conditionname\d+$/, $cgi->param;
630
631 };
632
633 my $condition_error_callback = sub {
634   map {
635     new FS::part_event_condition { 'conditionname' => $_, };
636   } keys %cgi_conditions;
637 };
638
639 my $condition_layer_values = sub {
640   #m2_table option causes this to be
641   # part_event_condition instead of part_event
642   my ( $cgi, $part_event_condition, $switches ) = @_;
643   scalar( #force hashref
644     {
645       #map { $_ => { $part_event_condition->options } }
646       #    keys %condition_fields
647       map { my $conditionname = $_;
648             my %opts = $switches->{'mode'} eq 'error'
649                        ? %{ $cgi_conditions{$conditionname} || {} }
650                        : $part_event_condition->options;
651             %opts = (
652               map { ( "$conditionname.$_" => $opts{$_} ); }
653                   keys %opts
654             );
655             ( $conditionname => \%opts );
656           }
657           keys %condition_fields
658     }
659   );
660 };
661
662
663 </%once>
664 <%init>
665
666 my $curuser = $FS::CurrentUser::CurrentUser;
667
668 die "access denied"
669   unless $curuser->access_right('Edit billing events')
670       || $curuser->access_right('Edit global billing events');
671
672 my $disable_empty_agent= ! $curuser->access_right('Edit global billing events');
673
674 %cgi_conditions = ();
675 my $use_cgi_conditions = 0;
676
677 my $JS_DEBUG = 0;
678
679 </%init>