RT#27396: Why does it take so long for the New/Edit billing event pages to load?...
[freeside.git] / httemplate / edit / part_event.html
1 <& 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                             { field   => '_initialize',
26                               type      => 'checkbox',
27                               onchange  => '_initialize_changed',
28                               value   => 'Y',
29                             }, 
30                             { type    => 'title',
31                               value   => 'Event Conditions',
32                             },
33                             { field   => 'conditionname',
34                               type    => 'selectlayersx',
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',
49                             },
50                             { type    => 'title',
51                               value   => 'Event Action',
52                             },
53                             { field   => 'action',
54                               type     => 'selectlayersx',
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'),
61                             },
62                           ],
63               'labels' => {
64                             'eventpart'  => 'Event',
65                             'event'      => 'Event name',
66                             'eventtable' => 'Type',
67                             'agentnum'   => 'Agent',
68                             'check_freq' => 'Check frequency',
69                             'disabled'   => 'Disable event',
70
71                             'conditionname' => 'Add&nbsp;new&nbsp;condition',
72                             #'weight',
73                             'action'     => 'Action',
74                             '_initialize' => 'Initialize event',
75                           },
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'} }, '';
80               },
81               'error_callback' => $error_callback,
82
83               'agent_virt'       => 1,
84               'agent_null_right' => 'Edit global billing events',
85 &>
86 <SCRIPT TYPE="text/javascript">
87
88   window.onload = function () { eventtable_changed(document.getElementById('eventtable')) };
89   var notonload = 0;
90
91   function eventtable_changed(what) {
92
93 %   if ( $JS_DEBUG ) {
94       alert('eventtable_changed called on ' + what );
95 %   }
96
97     var eventtable = what.options[what.selectedIndex].value;
98 %   if ( $JS_DEBUG ) {
99       alert ("eventtable: " + eventtable);
100 %   }
101     var eventdesc  = what.options[what.selectedIndex].text;
102
103     //remove the ** Select type **
104     if ( what.options[0].value == '' && notonload++ > 0 ) {
105       what.options[0] = null;
106     }
107
108     ////
109     // XXX gray out conditions that can't apply (in addition to the warning)?
110     ////
111
112     ////
113     // update condition selects
114     ////
115
116     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
117       var cond_id = 'conditionname' + cnum;
118       var cond_select = document.getElementById(cond_id);
119
120 %     if ( $JS_DEBUG ) {
121         alert('updating ' + cond_id);
122 %     }
123
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;
127
128       var seen_condition = condition_repop(cond_select);
129
130       var warning = document.getElementById(cond_id + '_warning');
131 %     if ( $JS_DEBUG ) {
132         alert('turning off warning; setting style.display of '+ cond_id +
133               '_warning (' + warning + ') to none');
134 %     }
135       warning.style.display = 'none';
136
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
144 %         if ( $JS_DEBUG ) {
145             alert('turning on warning; setting style.display of '+ cond_id +
146                   '_warning (' + warning + ') to ""');
147 %         }
148           warning.innerHTML = 'Not applicable to ' + eventdesc + ' events';
149           warning.style.display = '';
150         } else {
151           if ( ! condition_in_eventtable(conditionname) ) {
152 %           if ( $JS_DEBUG ) {
153               alert(conditionname + " not in " + eventtable + "; disabling");
154 %           }
155             cond_select.parentNode.parentNode.style.display = 'none';
156             cond_select.disabled = 'disabled';
157           } else {
158 %           if ( $JS_DEBUG ) {
159               alert(conditionname + " implicit for " + eventtable + "; enabling");
160 %           }
161             cond_select.parentNode.parentNode.style.display = '';
162             cond_select.disabled = '';
163           }
164         }
165       }
166
167     }
168
169
170     ////
171     // update action select
172     ////
173
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;
178
179     // blank the current action select
180     for ( var i = what.form.action.length; i >= 0; i-- )
181       what.form.action.options[i] = null;
182
183     if ( action == '' ) {
184       opt(what.form.action, action, a_desc, true );
185     }
186
187     // repopulate it
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);
191
192       if ( eventtable == '<% $eventtable %>' ) {
193
194 %       foreach my $action ( keys %actions ) {
195 %         ( my $description = $actions{$action}->{'description'} ) =~ s/'/\\'/g;
196
197           var sel = false;
198           if ( action == '<% $action %>' ) {
199             seen_action = true;
200             sel = true;
201           }
202           opt( what.form.action, '<% $action %>', '<% $description %>', sel );
203 %       }
204
205       }
206
207 %   }
208
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 = '';
214
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';
223     }
224
225   }
226
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;
231   }
232
233   function action_changed(what) {
234     // remove '** Select new **'
235     if ( what.options[0].value == '' ) {
236        what.options[0] = null;
237     }
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 = '';
244     }
245   }
246
247   function condition_changed(what) {
248     // remove '** Select new **'
249     if ( what.options[0].value == '' ) {
250        what.options[0] = null;
251     }
252
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;
257
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;
264             }
265           }
266           return false;
267         }
268       }
269 %   }
270
271     //alert(previous + ' changed to ' + value);
272
273     var field_regex = /(\d+)$/;
274     var match = field_regex.exec(what.name);
275     if ( !match ) {
276       alert(what.name + " didn't match?!");
277       return;
278     }
279
280     //add the previous condition *back* to all the other selects...
281     condition_add(previousValue, previousText, match[1]);
282
283     what.setAttribute('previousValue', value);
284     what.setAttribute('previousText', text);
285
286     // remove the new condition from all other selects
287     condition_remove(value, match[1]);
288
289   }
290
291   function condition_avail(check_cond, curnum) {
292     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
293       if ( cnum == curnum ) continue;
294
295       var cond_id = 'conditionname' + cnum;
296       var cond_select = document.getElementById(cond_id);
297
298       //alert("checking " + cond_id + " (" + cond_select.disabled + ")");
299
300       if ( cond_select.disabled ) continue;
301
302       // the current value
303       var conditionname = cond_select.options[cond_select.selectedIndex].value;
304
305       if ( check_cond == conditionname ) return false;
306
307     }
308
309     return true;
310
311   }
312
313   function condition_remove(remove_cond, curnum) {
314
315     if ( remove_cond.length == 0 ) return;
316
317     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
318       if ( cnum == curnum ) continue;
319
320       var cond_id = 'conditionname' + cnum;
321       var cond_select = document.getElementById(cond_id);
322
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;
327         }
328       }
329
330     }
331
332   }
333
334   function condition_add(add_condname, add_conddesc, curnum) {
335
336     if ( add_condname.length == 0 ) return;
337
338     var in_eventtable = condition_in_eventtable(add_condname);
339
340     if ( ! in_eventtable ) return;
341
342     for ( var cnum=0; document.getElementById('conditionname'+cnum); cnum++ ) {
343       if ( cnum == curnum ) continue;
344
345       var cond_id = 'conditionname' + cnum;
346       var cond_select = document.getElementById(cond_id);
347
348       if ( cond_select.disabled ) continue;
349
350       //alert("adding " + add_condname + " to " + cond_id);
351
352       opt(cond_select, add_condname, add_conddesc, false );
353
354       cond_select.parentNode.parentNode.style.display = '';
355
356     }
357
358   }
359
360   function condition_in_eventtable(condname) {
361
362     var eventtable_el = document.getElementById('eventtable');
363     var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
364
365     var in_eventtable = false;
366
367 %   foreach my $eventtable ( FS::part_event->eventtables ) {
368 %     tie my %conditions, 'Tie::IxHash',
369 %       FS::part_event_condition->conditions($eventtable);
370
371       if ( eventtable == '<% $eventtable %>' ) {
372
373 %       foreach my $conditionname ( keys %conditions ) {
374
375           if ( condname == '<% $conditionname %>' ) {
376             in_eventtable = true;
377           }
378
379 %       }
380
381       }
382
383 %   }
384
385     return in_eventtable;
386
387   }
388
389   function condition_is_implicit(condname) {
390
391     if ( true <% @implicit_conditions
392                    ? ( ' && '. join(' && ', map { "condname != '$_'" }
393                                                 @implicit_conditions
394                                    )
395                      )
396                    : ''
397               %> ) {
398       return false;
399     } else {
400       return true;
401     }
402   }
403
404   function condition_repop(cond_select) {
405
406     var eventtable_el = document.getElementById('eventtable');
407     var eventtable = eventtable_el.options[eventtable_el.selectedIndex].value;
408
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;
413
414     //skip deleted conditions
415     if ( cond_select.disabled && conditionname != '' && ! condition_is_implicit(conditionname) ) {
416       return false;
417     }
418
419     var field_regex = /(\d+)$/;
420     var match = field_regex.exec(cond_select.name);
421     if ( !match ) {
422       alert(what.name + " didn't match?!");
423       return;
424     }
425     var cnum = match[1];
426
427     // blank the current condition select
428     for ( var i = cond_select.length; i >= 0; i-- )
429       cond_select.options[i] = null;
430
431     if ( conditionname == '' ) {
432       opt(cond_select, conditionname, cond_desc, true );
433     }
434
435     // repopulate it
436 %   foreach my $eventtable ( FS::part_event->eventtables ) {
437 %     tie my %conditions, 'Tie::IxHash',
438 %       FS::part_event_condition->conditions($eventtable);
439
440       if ( eventtable == '<% $eventtable %>' ) {
441
442 %       foreach my $conditionname ( keys %conditions ) {
443 %         my $description = $conditions{$conditionname}->{'description'};
444 %         $description =~ s/'/\\'/g;
445
446           var sel = false;
447           if ( conditionname == '<% $conditionname %>' ) {
448             seen_condition = true;
449             sel = true;
450           }
451
452           if ( condition_avail("<% $conditionname %>", cnum) ) {
453             opt(cond_select, '<% $conditionname %>', '<% $description %>', sel);
454           }
455
456 %       }
457
458       }
459         
460 %   }
461
462     if ( cond_select.length > 1 || cond_select.length == 1 && cond_select.options[0].value.length > 0 ) {
463        
464       cond_select.parentNode.parentNode.style.display = '';
465       cond_select.disabled = '';
466
467     } else {
468       cond_select.parentNode.parentNode.style.display = 'none';
469       cond_select.disabled = 'disabled';
470     }
471
472     return seen_condition;
473
474   }
475
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.');
483     }
484   }
485
486 </SCRIPT>
487 <%once>
488
489 #misc (eventtable, check_freq)
490
491 my $eventtable_labels = FS::part_event->eventtable_labels;
492 $eventtable_labels->{''} = '** Select type **';
493
494 my $check_freq_labels = FS::part_event->check_freq_labels;
495
496 #conditions
497
498 tie my %all_conditions, 'Tie::IxHash', 
499   '' => { 'description' => '*** Select new condition ***', },
500   FS::part_event_condition->conditions();
501
502 # *** Select new condition *** sorts to the beginning anyway
503 (tied %all_conditions)->SortByValue;
504
505 my %condition_labels = map { $_ => $all_conditions{$_}->{'description'} }
506                            keys %all_conditions;
507
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{$_} ); }
514                                            keys %opts
515                                      );
516                              ( $c => [ %opts ] );
517                            } 
518                            keys %all_conditions;
519
520 my @implicit_conditions = sort { $all_conditions{$a}->{'implicit_flag'} <=>
521                                  $all_conditions{$b}->{'implicit_flag'}
522                                }
523                           grep { $all_conditions{$_}->{'implicit_flag'} }
524                           keys %all_conditions;
525
526 my @implicit_condition_objs = map {
527                                     new FS::part_event_condition {
528                                       'conditionname' => $_,
529                                     };
530                                   }
531                                   @implicit_conditions;
532
533 my %condition_remove_warnings =
534   map  { ( $_ => $all_conditions{$_}->{'remove_warning'} ); }
535   grep { $all_conditions{$_}->{'remove_warning'} }
536   keys %all_conditions;
537
538 #actions
539
540 tie my %all_actions, 'Tie::IxHash', 
541   '' => { 'description' => '*** Select event action ***', },
542   FS::part_event->actions();
543
544 (tied %all_actions)->SortByValue;
545
546 my %action_labels = map { $_ => $all_actions{$_}->{'description'} }
547                         keys %all_actions;
548
549 #my %action_fields = map { $_ => $all_actions{$_}->{option_fields} }
550 #                        keys %all_actions;
551 my %action_fields = map { my $action = $_;
552                           tie my %opts, 'Tie::IxHash',
553                             @{ $all_actions{$action}->{option_fields} || [] };
554                           %opts = ( map { ( "$action.$_" => $opts{$_} ); }
555                                         keys %opts
556                                   );
557                           ( $action => [ %opts ] );
558                         }
559                         keys %all_actions;
560
561 #subs
562
563 sub n_a {
564   my $t = shift;
565
566   return sub {
567     my $field = shift;
568     qq( <FONT ID="${field}_warning" STYLE="display:none" COLOR="#FF0000">).
569       "Party Party Join us Join us".
570       '</FONT>';
571   };
572 }
573
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
579     {
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{$_} ); }
586                             keys %obj_opts;
587             my %opts =
588               map { #false laziness w/process/part_event.html
589                     my $option = $_;
590                     my $value = scalar($cgi->param($_)) || $obj_opts{$_};
591
592                     if ( $option =~ /^(.*)\.reasonnum$/ && $value == -1 ) {
593                       $value = {
594                         'typenum' => scalar( $cgi->param( "new${option}T" ) ),
595                         'reason'  => scalar( $cgi->param( "new${option}"  ) ),
596                       };
597                     }
598
599                     ( $option => $value );
600
601                   }
602                   keys %fields;
603             ( $action => \%opts );
604           }
605           keys %action_fields
606     }
607   );
608 };
609
610 tie my %cgi_conditions, 'Tie::IxHash';
611
612 my $error_callback = sub {
613   my( $cgi, $object, $fields_listref ) = @_;
614
615   my @cond_params = grep /^conditionname\d+$/, $cgi->param;
616
617   %cgi_conditions = map {
618     my $param = $_;
619     my $conditionname = $cgi->param($param);
620     $conditionname => {
621       map { 
622
623         my $cgi_key = $_;
624         $cgi_key =~ /^$param\.$conditionname\.(.*)$/ or die 'wtf!';
625         my $key = $1;
626         #my $value = $cgi->param($_);
627
628         #my $info = $all_conditions->{$conditionname}
629         my %cond_opts =
630           @{ $all_conditions{$conditionname}->{'option_fields'} || []};
631         my $info = $cond_opts{$key};
632
633         my $value;
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');
640         } else {
641           $value = $cgi->param($cgi_key);
642         }
643
644         $key => $value;
645
646       } grep /^$param\.$conditionname\./, $cgi->param
647     };
648   } grep $cgi->param($_), grep /^conditionname\d+$/, $cgi->param;
649
650 };
651
652 my $condition_error_callback = sub {
653   map {
654     new FS::part_event_condition { 'conditionname' => $_, };
655   } keys %cgi_conditions;
656 };
657
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
663     {
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;
670             %opts = (
671               map { ( "$conditionname.$_" => $opts{$_} ); }
672                   keys %opts
673             );
674             ( $conditionname => \%opts );
675           }
676           keys %condition_fields
677     }
678   );
679 };
680
681
682 </%once>
683 <%init>
684
685 my $curuser = $FS::CurrentUser::CurrentUser;
686
687 die "access denied"
688   unless $curuser->access_right('Edit billing events')
689       || $curuser->access_right('Edit global billing events');
690
691 my $disable_empty_agent= ! $curuser->access_right('Edit global billing events');
692
693 %cgi_conditions = ();
694 my $use_cgi_conditions = 0;
695
696 my $JS_DEBUG = 0;
697
698 </%init>