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