daily/weekly billing
[freeside.git] / httemplate / edit / part_pkg.cgi
1 <!-- mason kludge -->
2 <%
3
4 if ( $cgi->param('clone') && $cgi->param('clone') =~ /^(\d+)$/ ) {
5   $cgi->param('clone', $1);
6 } else {
7   $cgi->param('clone', '');
8 }
9 if ( $cgi->param('pkgnum') && $cgi->param('pkgnum') =~ /^(\d+)$/ ) {
10   $cgi->param('pkgnum', $1);
11 } else {
12   $cgi->param('pkgnum', '');
13 }
14
15 my ($query) = $cgi->keywords;
16 my $action = '';
17 my $part_pkg = '';
18 if ( $cgi->param('error') ) {
19   $part_pkg = new FS::part_pkg ( {
20     map { $_, scalar($cgi->param($_)) } fields('part_pkg')
21   } );
22 }
23 if ( $cgi->param('clone') ) {
24   $action='Custom Pricing';
25   my $old_part_pkg =
26     qsearchs('part_pkg', { 'pkgpart' => $cgi->param('clone') } );
27   $part_pkg ||= $old_part_pkg->clone;
28   $part_pkg->disabled('Y');
29 } elsif ( $query && $query =~ /^(\d+)$/ ) {
30   $part_pkg ||= qsearchs('part_pkg',{'pkgpart'=>$1});
31 } else {
32   unless ( $part_pkg ) {
33     $part_pkg = new FS::part_pkg {};
34     $part_pkg->plan('flat');
35   }
36 }
37 unless ( $part_pkg->plan ) { #backwards-compat
38   $part_pkg->plan('flat');
39   $part_pkg->plandata("setup_fee=". $part_pkg->setup. "\n".
40                       "recur_fee=". $part_pkg->recur. "\n");
41 }
42 $action ||= $part_pkg->pkgpart ? 'Edit' : 'Add';
43 my $hashref = $part_pkg->hashref;
44
45
46 print header("$action Package Definition", menubar(
47   'Main Menu' => popurl(2),
48   'View all packages' => popurl(2). 'browse/part_pkg.cgi',
49 ));
50 #), ' onLoad="visualize()"');
51
52 print qq!<FONT SIZE="+1" COLOR="#ff0000">Error: !, $cgi->param('error'),
53       "</FONT>"
54   if $cgi->param('error');
55
56 #print '<FORM ACTION="', popurl(1), 'process/part_pkg.cgi" METHOD=POST>';
57 print '<FORM NAME="dummy">';
58
59 #if ( $cgi->param('clone') ) {
60 #  print qq!<INPUT TYPE="hidden" NAME="clone" VALUE="!, $cgi->param('clone'), qq!">!;
61 #}
62 #if ( $cgi->param('pkgnum') ) {
63 #  print qq!<INPUT TYPE="hidden" NAME="pkgnum" VALUE="!, $cgi->param('pkgnum'), qq!">!;
64 #}
65 #
66 #print qq!<INPUT TYPE="hidden" NAME="pkgpart" VALUE="$hashref->{pkgpart}">!,
67 print "Package Part #", $hashref->{pkgpart} ? $hashref->{pkgpart} : "(NEW)";
68
69 my %freq;
70 tie %freq, 'Tie::IxHash', 
71   '0'  => '(no recurring fee)',
72   '1d' => 'daily',
73   '1w' => 'weekly',
74   '2w' => 'biweekly (every 2 weeks)',
75   '1'  => 'monthly',
76   '2'  => 'bimonthly (every 2 months)',
77   '3'  => 'quarterly (every 3 months)',
78   '6'  => 'semiannually (every 6 months)',
79   '12' => 'annually',
80   '24' => 'biannually (every 2 years)',
81 ;
82 if ( $part_pkg->dbdef_table->column('freq')->type =~ /(int)/i ) {
83   delete $freq{$_} foreach grep { ! /^\d+$/ } keys %freq;
84 }
85
86 %>
87 <%= ntable("#cccccc",2) %>
88   <TR>
89     <TD ALIGN="right">Package (customer-visible)</TD>
90     <TD>
91       <INPUT TYPE="text" NAME="pkg" SIZE=32 VALUE="<%= $part_pkg->pkg %>">
92     </TD>
93   </TR>
94   <TR>
95     <TD ALIGN="right">Comment (customer-hidden)</TD>
96     <TD>
97       <INPUT TYPE="text" NAME="comment" SIZE=32 VALUE="<%=$part_pkg->comment%>">
98     </TD>
99   </TR>
100   <TR>
101     <TD ALIGN="right">Recurring fee frequency </TD>
102     <TD>
103       <SELECT NAME="freq">
104         <% foreach my $freq ( keys %freq ) { %>
105           <OPTION VALUE="<%= $freq %>"<%= $freq eq $part_pkg->freq ? ' SELECTED' : '' %>><%= $freq{$freq} %>
106         <% } %>
107       </SELECT>
108     </TD>
109   </TR>
110   <TR>
111     <TD ALIGN="right">Setup fee tax exempt</TD>
112     <TD>
113 <%
114
115 print '<INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y"';
116 print ' CHECKED' if $hashref->{setuptax} eq "Y";
117 print '>';
118
119 print <<END;
120 </TD></TR>
121 <TR><TD ALIGN="right">Recurring fee tax exempt</TD><TD>
122 END
123
124 print '<INPUT TYPE="checkbox" NAME="recurtax" VALUE="Y"';
125 print ' CHECKED' if $hashref->{recurtax} eq "Y";
126 print '>';
127
128 print '</TD></TR>';
129
130 my $conf = new FS::Conf;
131 #false laziness w/ view/cust_main.cgi quick order
132 if ( $conf->exists('enable_taxclasses') ) {
133   print '<TR><TD ALIGN="right">Tax class</TD><TD><SELECT NAME="taxclass">';
134   my $sth = dbh->prepare('SELECT DISTINCT taxclass FROM cust_main_county')
135     or die dbh->errstr;
136   $sth->execute or die $sth->errstr;
137   foreach my $taxclass ( map $_->[0], @{$sth->fetchall_arrayref} ) {
138     print qq!<OPTION VALUE="$taxclass"!;
139     print ' SELECTED' if $taxclass eq $hashref->{taxclass};
140     print qq!>$taxclass</OPTION>!;
141   }
142   print '</SELECT></TD></TR>';
143 } else {
144   print
145     '<INPUT TYPE="hidden" NAME="taxclass" VALUE="'. $hashref->{taxclass}. '">';
146 }
147
148 print '<TR><TD ALIGN="right">Disable new orders</TD><TD>';
149 print '<INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"';
150 print ' CHECKED' if $hashref->{disabled} eq "Y";
151 print '>';
152 print '</TD></TR></TABLE>';
153
154 my $thead =  "\n\n". ntable('#cccccc', 2). <<END;
155 <TR><TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Quan.</FONT></TH><TH BGCOLOR="#dcdcdc">Service</TH></TR>
156 END
157
158 #unless ( $cgi->param('clone') ) {
159 #dunno why...
160 unless ( 0 ) {
161   #print <<END, $thead;
162   print <<END, itable(), '<TR><TD VALIGN="top">', $thead;
163 <BR><BR>Enter the quantity of each service this package includes.<BR><BR>
164 END
165 }
166
167 my @fixups = ();
168 my $count = 0;
169 my $columns = 3;
170 my @part_svc = qsearch( 'part_svc', { 'disabled' => '' } );
171 foreach my $part_svc ( @part_svc ) {
172   my $svcpart = $part_svc->svcpart;
173   my $pkgpart = $cgi->param('clone') || $part_pkg->pkgpart;
174   my $pkg_svc = $pkgpart && qsearchs( 'pkg_svc', {
175     'pkgpart'  => $pkgpart,
176     'svcpart'  => $svcpart,
177   } ) || new FS::pkg_svc ( {
178     'pkgpart'  => $pkgpart,
179     'svcpart'  => $svcpart,
180     'quantity' => 0,
181   });
182   #? #next unless $pkg_svc;
183
184   push @fixups, "pkg_svc$svcpart";
185
186   #unless ( defined ($cgi->param('clone')) && $cgi->param('clone') ) {
187   #dunno why...
188   unless ( 0 ) {
189     print '<TR>'; # if $count == 0 ;
190     print qq!<TD><INPUT TYPE="text" NAME="pkg_svc$svcpart" SIZE=4 MAXLENGTH=3 VALUE="!,
191           $cgi->param("pkg_svc$svcpart") || $pkg_svc->quantity || 0,
192           qq!"></TD><TD><A HREF="part_svc.cgi?!,$part_svc->svcpart,
193           qq!">!, $part_svc->getfield('svc'), "</A></TD></TR>";
194 #    print "</TABLE></TD><TD>$thead" if ++$count == int(scalar(@part_svc) / 2);
195     $count+=1;
196     foreach ( 1 .. $columns-1 ) {
197       print "</TABLE></TD><TD VALIGN=\"top\">$thead"
198         if $count == int( $_ * scalar(@part_svc) / $columns );
199     }
200   } else {
201     print qq!<INPUT TYPE="hidden" NAME="pkg_svc$svcpart" VALUE="!,
202           $cgi->param("pkg_svc$svcpart") || $pkg_svc->quantity || 0, qq!">\n!;
203   }
204 }
205
206 #unless ( $cgi->param('clone') ) {
207 #dunno why...
208 unless ( 0 ) {
209   print "</TR></TABLE></TD></TR></TABLE>";
210   #print "</TR></TABLE>";
211 }
212
213 foreach my $f ( qw( clone pkgnum ) ) {
214   print qq!<INPUT TYPE="hidden" NAME="$f" VALUE="!. $cgi->param($f). '">';
215 }
216 print '<INPUT TYPE="hidden" NAME="pkgpart" VALUE="'. $part_pkg->pkgpart. '">';
217
218 # prolly should be in database
219 tie my %plans, 'Tie::IxHash',
220   'flat' => {
221     'name' => 'Flat rate (anniversary billing)',
222     'fields' => {
223       'setup_fee' => { 'name' => 'Setup fee for this package',
224                        'default' => 0,
225                      },
226       'recur_fee' => { 'name' => 'Recurring fee for this package',
227                        'default' => 0,
228                       },
229     },
230     'fieldorder' => [ 'setup_fee', 'recur_fee' ],
231     'setup' => 'what.setup_fee.value',
232     'recur' => 'what.recur_fee.value',
233   },
234
235   'flat_delayed' => {
236     'name' => 'Free for X days, then flat rate (anniversary billing)',
237     'fields' =>  {
238       'free_days' => { 'name' => 'Initial free days',
239                        'default' => 0,
240                      },
241       'setup_fee' => { 'name' => 'Setup fee for this package',
242                        'default' => 0,
243                      },
244       'recur_fee' => { 'name' => 'Recurring fee for this package',
245                        'default' => 0,
246                       },
247     },
248     'fieldorder' => [ 'free_days', 'setup_fee', 'recur_fee' ],
249     'setup' => '\'my $d = $cust_pkg->bill || $time; $d += 86400 * \' + what.free_days.value + \'; $cust_pkg->bill($d); $cust_pkg_mod_flag=1; \' + what.setup_fee.value',
250     'recur' => 'what.recur_fee.value',
251   },
252
253   'prorate' => {
254     'name' => 'First partial month pro-rated, then flat-rate (1st of month billing)',
255     'fields' =>  {
256       'setup_fee' => { 'name' => 'Setup fee for this package',
257                        'default' => 0,
258                      },
259       'recur_fee' => { 'name' => 'Recurring fee for this package',
260                        'default' => 0,
261                       },
262     },
263     'fieldorder' => [ 'setup_fee', 'recur_fee' ],
264     'setup' => 'what.setup_fee.value',
265     'recur' => '\'my $mnow = $sdate; my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($sdate) )[0,1,2,3,4,5]; my $mstart = timelocal(0,0,0,1,$mon,$year); my $mend = timelocal(0,0,0,1, $mon == 11 ? 0 : $mon+1, $year+($mon==11)); $sdate = $mstart; ( $part_pkg->freq - 1 ) * \' + what.recur_fee.value + \' / $part_pkg->freq + \' + what.recur_fee.value + \' / $part_pkg->freq * ($mend-$mnow) / ($mend-$mstart) ; \'',
266   },
267
268   'subscription' => {
269     'name' => 'First partial month full charge, then flat-rate (1st of month billing)',
270     'fields' => {
271       'setup_fee' => { 'name' => 'Setup fee for this package',
272                        'default' => 0,
273                      },
274       'recur_fee' => { 'name' => 'Recurring fee for this package',
275                        'default' => 0,
276                       },
277     },
278     'fieldorder' => [ 'setup_fee', 'recur_fee' ],
279     'setup' => 'what.setup_fee.value',
280     'recur' => '\'my $mnow = $sdate; my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($sdate) )[0,1,2,3,4,5]; $sdate = timelocal(0,0,0,1,$mon,$year); \' + what.recur_fee.value',
281   },
282
283   'flat_comission_cust' => {
284     'name' => 'Flat rate with recurring commission per active customer',
285     'fields' => {
286       'setup_fee' => { 'name' => 'Setup fee for this package',
287                        'default' => 0,
288                      },
289       'recur_fee' => { 'name' => 'Recurring fee for this package',
290                        'default' => 0,
291                      },
292       'comission_amount' => { 'name' => 'Commission amount per month (per active customer)',
293                               'default' => 0,
294                             },
295       'comission_depth'  => { 'name' => 'Number of layers',
296                               'default' => 1,
297                             },
298     },
299     'fieldorder' => [ 'setup_fee', 'recur_fee', 'comission_depth', 'comission_amount' ],
300     'setup' => 'what.setup_fee.value',
301     'recur' => '\'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar($cust_pkg->cust_main->referral_cust_main_ncancelled(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
302   },
303
304   'flat_comission' => {
305     'name' => 'Flat rate with recurring commission per (any) active package',
306     'fields' => {
307       'setup_fee' => { 'name' => 'Setup fee for this package',
308                        'default' => 0,
309                      },
310       'recur_fee' => { 'name' => 'Recurring fee for this package',
311                        'default' => 0,
312                      },
313       'comission_amount' => { 'name' => 'Commission amount per month (per active package)',
314                               'default' => 0,
315                             },
316       'comission_depth'  => { 'name' => 'Number of layers',
317                               'default' => 1,
318                             },
319     },
320     'fieldorder' => [ 'setup_fee', 'recur_fee', 'comission_depth', 'comission_amount' ],
321     'setup' => 'what.setup_fee.value',
322     'recur' => '\'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar($cust_pkg->cust_main->referral_cust_pkg(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
323   },
324
325   'flat_comission_pkg' => {
326     'name' => 'Flat rate with recurring commission per (selected) active package',
327     'fields' => {
328       'setup_fee' => { 'name' => 'Setup fee for this package',
329                        'default' => 0,
330                      },
331       'recur_fee' => { 'name' => 'Recurring fee for this package',
332                        'default' => 0,
333                      },
334       'comission_amount' => { 'name' => 'Commission amount per month (per uncancelled package)',
335                               'default' => 0,
336                             },
337       'comission_depth'  => { 'name' => 'Number of layers',
338                               'default' => 1,
339                             },
340       'comission_pkgpart' => { 'name' => 'Applicable packages<BR><FONT SIZE="-1">(hold <b>ctrl</b> to select multiple packages)</FONT>',
341                                'type' => 'select_multiple',
342                                'select_table' => 'part_pkg',
343                                'select_hash'  => { 'disabled' => '' } ,
344                                'select_key'   => 'pkgpart',
345                                'select_label' => 'pkg',
346                              },
347     },
348     'fieldorder' => [ 'setup_fee', 'recur_fee', 'comission_depth', 'comission_amount', 'comission_pkgpart' ],
349     'setup' => 'what.setup_fee.value',
350     'recur' => '""; var pkgparts = ""; for ( var c=0; c < document.flat_comission_pkg.comission_pkgpart.options.length; c++ ) { if (document.flat_comission_pkg.comission_pkgpart.options[c].selected) { pkgparts = pkgparts + document.flat_comission_pkg.comission_pkgpart.options[c].value + \', \'; } } what.recur.value = \'my $error = $cust_pkg->cust_main->credit( \' + what.comission_amount.value + \' * scalar( grep { my $pkgpart = $_->pkgpart; grep { $_ == $pkgpart } ( \' + pkgparts + \'  ) } $cust_pkg->cust_main->referral_cust_pkg(\' + what.comission_depth.value+ \')), "commission" ); die $error if $error; \' + what.recur_fee.value + \';\'',
351   },
352
353
354
355   'sesmon_hour' => {
356     'name' => 'Base charge plus charge per-hour from the session monitor',
357     'fields' => {
358       'setup_fee' => { 'name' => 'Setup fee for this package',
359                        'default' => 0,
360                      },
361       'recur_flat' => { 'name' => 'Base monthly charge for this package',
362                         'default' => 0,
363                       },
364       'recur_included_hours' => { 'name' => 'Hours included',
365                                   'default' => 0,
366                                 },
367       'recur_hourly_charge' => { 'name' => 'Additional charge per hour',
368                                  'default' => 0,
369                                },
370     },
371     'fieldorder' => [ 'setup_fee', 'recur_flat', 'recur_included_hours', 'recur_hourly_charge' ],
372     'setup' => 'what.setup_fee.value',
373     'recur' => '\'my $hours = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; \' + what.recur_flat.value + \' + \' + what.recur_hourly_charge.value + \' * $hours;\'',
374   },
375
376   'sesmon_minute' => {
377     'name' => 'Base charge plus charge per-minute from the session monitor',
378     'fields' => {
379       'setup_fee' => { 'name' => 'Setup fee for this package',
380                        'default' => 0,
381                      },
382       'recur_flat' => { 'name' => 'Base monthly charge for this package',
383                         'default' => 0,
384                       },
385       'recur_included_min' => { 'name' => 'Minutes included',
386                                 'default' => 0,
387                                 },
388       'recur_minly_charge' => { 'name' => 'Additional charge per minute',
389                                 'default' => 0,
390                               },
391     },
392     'fieldorder' => [ 'setup_fee', 'recur_flat', 'recur_included_min', 'recur_minly_charge' ],
393     'setup' => 'what.setup_fee.value',
394     'recur' => '\'my $min = $cust_pkg->seconds_since($cust_pkg->bill || 0) / 60 - \' + what.recur_included_min.value + \'; $min = 0 if $min < 0; \' + what.recur_flat.value + \' + \' + what.recur_minly_charge.value + \' * $min;\'',
395
396   },
397
398   'sqlradacct_hour' => {
399     'name' => 'Base charge plus charge per-hour (and for data) from an external sqlradius radacct table',
400     'fields' => {
401       'setup_fee' => { 'name' => 'Setup fee for this package',
402                        'default' => 0,
403                      },
404       'recur_flat' => { 'name' => 'Base monthly charge for this package',
405                         'default' => 0,
406                       },
407       'recur_included_hours' => { 'name' => 'Hours included',
408                                   'default' => 0,
409                                 },
410       'recur_hourly_charge' => { 'name' => 'Additional charge per hour',
411                                  'default' => 0,
412                                },
413       'recur_included_input' => { 'name' => 'Input megabytes included',
414                                   'default' => 0,
415                                 },
416       'recur_input_charge' => { 'name' =>
417                                         'Additional charge per input megabyte',
418                                 'default' => 0,
419                               },
420       'recur_included_output' => { 'name' => 'Output megabytes included',
421                                    'default' => 0,
422                                 },
423       'recur_output_charge' => { 'name' =>
424                                        'Additional charge per output megabyte',
425                                 'default' => 0,
426                               },
427       'recur_included_total' => { 'name' =>
428                                        'Total input+output megabytes included',
429                                   'default' => 0,
430                                 },
431       'recur_total_charge' => { 'name' =>
432                                  'Additional charge per input+output megabyte',
433                                 'default' => 0,
434                               },
435     },
436     'fieldorder' => [qw( setup_fee recur_flat recur_included_hours recur_hourly_charge recur_included_input recur_input_charge recur_included_output recur_output_charge recur_included_total recur_total_charge )],
437     'setup' => 'what.setup_fee.value',
438     'recur' => '\'my $last_bill = $cust_pkg->last_bill; my $hours = $cust_pkg->seconds_since_sqlradacct($last_bill, $sdate ) / 3600 - \' + what.recur_included_hours.value + \'; $hours = 0 if $hours < 0; my $input = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctInputOctets\" ) / 1048576; my $output = $cust_pkg->attribute_since_sqlradacct($last_bill, $sdate, \"AcctOutputOctets\" ) / 1048576; my $total = $input + $output - \' + what.recur_included_total.value + \'; $total = 0 if $total < 0; my $input = $input - \' + what.recur_included_input.value + \'; $input = 0 if $input < 0; my $output = $output - \' + what.recur_included_output.value + \'; $output = 0 if $output < 0; my $totalcharge = sprintf(\"%.2f\", \' + what.recur_total_charge.value + \' * $total); my $hourscharge = sprintf(\"%.2f\", \' + what.recur_hourly_charge.value + \' * $hours); push @details, \"Last month\\\'s excess data \". sprintf(\"%.1f\", $total). \" megs: \\\$$totalcharge\", \"Last month\\\'s excess time \". sprintf(\"%.1f\", $hours). \" hours: \\\$$hourscharge\"; \' + what.recur_flat.value + \' + $hourscharge + \' + what.recur_input_charge.value + \' * $input + \' + what.recur_output_charge.value + \' * $output + $totalcharge ;\'',
439   },
440
441 ;
442
443 my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }
444                     split("\n", $part_pkg->plandata );
445
446 tie my %options, 'Tie::IxHash', map { $_=>$plans{$_}->{'name'} } keys %plans;
447
448 my @form_select = ();
449 if ( $conf->exists('enable_taxclasses') ) {
450   push @form_select, 'taxclass';
451 } else {
452   push @fixups, 'taxclass'; #hidden
453 }
454
455
456 my $widget = new HTML::Widgets::SelectLayers(
457   'selected_layer' => $part_pkg->plan,
458   'options'        => \%options,
459   'form_name'      => 'dummy',
460   'form_action'    => 'process/part_pkg.cgi',
461   'form_text'      => [ qw(pkg comment freq clone pkgnum pkgpart), @fixups ],
462   'form_checkbox'  => [ qw(setuptax recurtax disabled) ],
463   'form_select'    => [ @form_select ],
464   'fixup_callback' => sub {
465                         #my $ = @_;
466                         my $html = '';
467                         for my $p ( keys %plans ) {
468                           $html .= "if ( what.plan.value == \"$p\" ) {
469                                       what.setup.value = $plans{$p}->{setup} ;
470                                       what.recur.value = $plans{$p}->{recur} ;
471                                     }\n";
472                         }
473                         $html;
474                       },
475   'layer_callback' => sub {
476     my $layer = shift;
477     my $html = qq!<INPUT TYPE="hidden" NAME="plan" VALUE="$layer">!.
478                ntable("#cccccc",2);
479     my $href = $plans{$layer}->{'fields'};
480     foreach my $field ( exists($plans{$layer}->{'fieldorder'})
481                           ? @{$plans{$layer}->{'fieldorder'}}
482                           : keys %{ $href }
483                       ) {
484
485       $html .= '<TR><TD ALIGN="right">'. $href->{$field}{'name'}. '</TD><TD>';
486
487       if ( ! exists($href->{$field}{'type'}) ) {
488         $html .= qq!<INPUT TYPE="text" NAME="$field" VALUE="!.
489                  ( exists($plandata{$field})
490                      ? $plandata{$field}
491                      : $href->{$field}{'default'} ).
492                  qq!" onChange="fchanged(this)">!;
493       } elsif ( $href->{$field}{'type'} eq 'select_multiple' ) {
494         $html .= qq!<SELECT MULTIPLE NAME="$field" onChange="fchanged(this)">!;
495         foreach my $record (
496           qsearch( $href->{$field}{'select_table'},
497                    $href->{$field}{'select_hash'}   )
498         ) {
499           my $value = $record->getfield($href->{$field}{'select_key'});
500           $html .= qq!<OPTION VALUE="$value"!.
501                    (  $plandata{$field} =~ /(^|, *)$value *(,|$)/
502                         ? ' SELECTED'
503                         : ''          ).
504                    '>'. $record->getfield($href->{$field}{'select_label'})
505         }
506         $html .= '</SELECT>';
507       }
508
509       $html .= '</TD></TR>';
510     }
511     $html .= '</TABLE>';
512
513     $html .= '<INPUT TYPE="hidden" NAME="plandata" VALUE="'.
514              join(',', keys %{ $href } ). '">'.
515              '<BR><BR>';
516              
517     $html .= '<INPUT TYPE="submit" VALUE="'.
518              ( $hashref->{pkgpart} ? "Apply changes" : "Add package" ).
519              '" onClick="fchanged(this)">';
520
521     $html .= '<BR><BR>don\'t edit this unless you know what you\'re doing '.
522              '<INPUT TYPE="button" VALUE="refresh expressions" '.
523                'onClick="fchanged(this)">'.
524              ntable("#cccccc",2).
525              '<TR><TD>'.
526              '<FONT SIZE="1">Setup expression<BR>'.
527              '<INPUT TYPE="text" NAME="setup" SIZE="160" VALUE="'.
528                encode_entities($hashref->{setup}). '" onLoad="fchanged(this)">'.
529              '</FONT><BR>'.
530              '<FONT SIZE="1">Recurring espression<BR>'.
531              '<INPUT TYPE="text" NAME="recur" SIZE="160" VALUE="'.
532                encode_entities($hashref->{recur}). '" onLoad="fchanged(this)">'.
533              '</FONT>'.
534              '</TR></TD>'.
535              '</TABLE>';
536
537     $html;
538
539   },
540 );
541
542 %>
543
544 <BR>
545 Price plan <%= $widget->html %>
546   </BODY>
547 </HTML>