correctly sort reason selectors, #32521, from #31702
[freeside.git] / httemplate / elements / tr-select-reason.html
1 <%doc>
2
3 Example:
4
5   include( '/elements/tr-select-reason.html',
6
7     #required 
8     'field'         => 'reasonnum',
9     'reason_class'  => 'C', # currently 'C', 'R', 'F',  or 'S'
10                            # for cancel, credit, refund, or suspend
11
12     #recommended
13     'cgi' => $cgi, #easiest way for things to be properly "sticky" on errors
14
15     #optional
16     'control_button' => 'element_name', #button to be enabled when a reason is
17                                         #selected
18     'id'             => 'element_id',
19
20     #deprecated ways to keep things "sticky" on errors
21     # (requires duplicate code in each using file to parse cgi params)
22     'curr_value'     => $curr_value,
23     'curr_value'     => {
24                           'typenum' => $typenum,
25                           'reason'  => $reason,
26                         },
27
28   )
29
30 </%doc>
31
32 % # note style improvements.
33 % # - no more conditionally included code here
34 % # - callers are not expected to pass javascript fragments
35 % # - no redundant checking of ACLs or parameters
36 % # - form fields are grouped for easy management
37 % # - use the standard select-table widget instead of ad hoc crap
38 <SCRIPT TYPE="text/javascript">
39   function <% $id %>_changed() {
40     var hints = <% encode_json(\%all_hints) %>;
41     var select_reason = document.getElementById('<% $id %>');
42
43     document.getElementById('<% $id %>_hint').innerHTML =
44       hints[select_reason.value] || '';
45
46     // toggle submit button state
47     var submit_button = document.getElementById(<% $opt{control_button} |js_string %>);
48     if (submit_button) {
49       submit_button.disabled = ( select_reason.value == 0 );
50     }
51
52     // toggle visibility of 'new reason' fields
53     var new_fields = document.getElementById('<% $id %>_new_fields');
54     if ( select_reason.value == -1 ) {
55       new_fields.disabled = false;
56       new_fields.style.display = '';
57     } else {
58       new_fields.disabled = true;
59       new_fields.style.display = 'none';
60     }
61
62   }
63   <&| onload.js &> <% $id %>_changed(); </&>
64 </SCRIPT>
65
66 %# sadly can't just use add_inline here, as we have non-text fields
67 <& tr-select-table.html,
68   'label'           => 'Reason',
69   'field'           => $name,
70   'id'              => $id,
71   'table'           => 'reason',
72   'records'         => \@reasons,
73   'label_callback'  => sub { my $reason = shift;
74                              $reason->type . ' : ' .  $reason->reason },
75   'disable_empty'   => 1,
76   'pre_options'     => [ 0 => 'Select reason...' ],
77   'post_options'    => \@post_options,
78   'curr_value'      => $init_reason,
79   'onchange'        => $id.'_changed()',
80 &>
81
82 % # "add new reason" fields
83 % # should be a <fieldset>, but that doesn't fit well into the table
84
85 % if ( $curuser->access_right($add_access_right) ) {
86 <TR id="<% $id %>_new_fields">
87   <TD COLSPAN=2>
88     <TABLE CLASS="inv" STYLE="text-align: left">
89
90       <& tr-input-text.html,
91         label => 'New reason',
92         field => $id.'_new_reason'
93       &>
94
95 %   my @types = qsearch( 'reason_type', { 'class' => $class } );
96 %   if (scalar(@types) < 1) {  # we should never reach this
97       <TR>
98         <TD ALIGN="right">
99           <P><% mt('No reason types. Please add some.') |h %></P>
100         </TD>
101       </TR>
102 %   } elsif (scalar(@types) == 1) {
103       <& tr-fixed.html,
104         label => 'Reason type',
105         field => $id.'_new_reason_type',
106         curr_value => $types[0]->typenum,
107         formatted_value => $types[0]->type,
108       &>
109 %   } else { # more than one type, the normal case
110       <& tr-select-table.html,
111         label         => 'Reason type',
112         field         => $id.'_new_reason_type',
113         table         => 'reason_type',
114         name_col      => 'type',
115         hashref       => { 'class' => $class },
116         disable_empty => 1,
117       &>
118 %   } # scalar(@types)
119
120 %   if ( $class eq 'S' ) {
121       <& tr-checkbox.html,
122         label => 'Credit the unused portion of service when suspending',
123         field => $id.'_new_unused_credit',
124         value => 'Y'
125       &>
126       <& tr-select-part_pkg.html,
127         label   => 'Charge this fee when unsuspending',
128         field   => $id.'_new_unsuspend_pkgpart',
129         hashref => { disabled => '', freq => '0' },
130         empty_label => 'none',
131       &>
132       <& tr-checkbox.html,
133         label => 'Hold unsuspension fee until the next bill',
134         field => $id.'_new_unsuspend_hold',
135         value => 'Y',
136       &>
137 %   }
138     </table>
139   </td>
140 </tr>
141 % } # if the current user can add a reason
142
143 % # container for hints
144 <TR>
145   <TD COLSPAN=2 ALIGN="center" id="<% $id %>_hint" style="font-size:small">
146   </TD>
147 </TR>
148
149 <%init>
150
151 my $curuser = $FS::CurrentUser::CurrentUser;
152 my %opt = @_;
153
154 my $name = $opt{'field'};
155 my $class = $opt{'reason_class'};
156
157 my $init_reason;
158 if ( $opt{'cgi'} ) {
159   $init_reason = $opt{'cgi'}->param($name);
160 } else {
161   $init_reason = $opt{'curr_value'};
162 }
163
164 my $id = $opt{'id'} || $name;
165 $id =~ s/\./_/g; # for edit/part_event
166
167 my $add_access_right;
168 if ($class eq 'C') {
169   $add_access_right = 'Add on-the-fly cancel reason';
170 } elsif ($class eq 'S') {
171   $add_access_right = 'Add on-the-fly suspend reason';
172 } elsif ($class eq 'R') {
173   $add_access_right = 'Add on-the-fly credit reason';
174 } elsif ($class eq 'F') {
175   $add_access_right = 'Add on-the-fly refund reason';
176 } else {
177   die "illegal class: $class";
178 }
179
180 my @reasons = qsearch({
181   'table'           => 'reason',
182   'addl_from'       => ' LEFT JOIN reason_type'.
183                        ' ON (reason.reason_type = reason_type.typenum)',
184   'hashref'         => { disabled => '' },
185   'extra_sql'       => " AND reason_type.class = '$class'",
186   'order_by'        => ' ORDER BY type, reason',
187 });
188
189 my %all_hints;
190 if ( $class eq 'S' ) {
191   my $conf = FS::Conf->new;
192   %all_hints = ( 0 => '', -1 => '' );
193   foreach my $reason (@reasons) {
194     my @hints;
195     if ( $reason->unsuspend_pkgpart ) {
196       my $part_pkg = FS::part_pkg->by_key($reason->unsuspend_pkgpart);
197       if ( $part_pkg ) {
198         if ( $part_pkg->option('setup_fee',1) > 0 and 
199              $part_pkg->option('recur_fee',1) == 0 ) {
200           # the usual case
201           push @hints,
202             mt('A [_1] unsuspension fee will apply.', 
203                ($conf->config('money_char') || '$') .
204                sprintf('%.2f', $part_pkg->option('setup_fee'))
205                );
206         } else {
207           # oddball cases--not really supported
208           push @hints,
209             mt('An unsuspension package will apply: [_1]',
210               $part_pkg->price_info
211               );
212         }
213       } else { #no $part_pkg
214         push @hints,
215           '<FONT COLOR="#ff0000">Unsuspend pkg #'.$reason->unsuspend_pkgpart.
216           ' not found.</FONT>';
217       }
218     }
219     if ( $reason->unused_credit ) {
220       push @hints, mt('The customer will be credited for unused time.');
221     }
222     $all_hints{ $reason->reasonnum } = join('<BR>', @hints);
223   }
224 }
225
226 my @post_options;
227 if ( $curuser->access_right($add_access_right) ) {
228   @post_options = ( -1 => 'Add new reason' );
229 }
230
231 </%init>