770facb7e242f40c7e430229cacbf54a25b1fbac
[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 print ntable("#cccccc",2), <<END;
70 <TR><TD ALIGN="right">Package (customer-visable)</TD><TD><INPUT TYPE="text" NAME="pkg" SIZE=32 VALUE="$hashref->{pkg}"></TD></TR>
71 <TR><TD ALIGN="right">Comment (customer-hidden)</TD><TD><INPUT TYPE="text" NAME="comment" SIZE=32 VALUE="$hashref->{comment}"></TD></TR>
72 <TR><TD ALIGN="right">Frequency (months) of recurring fee</TD><TD><INPUT TYPE="text" NAME="freq" VALUE="$hashref->{freq}" SIZE=3></TD></TR>
73 <TR><TD ALIGN="right">Setup fee tax exempt</TD><TD>
74 END
75
76 print '<INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y"';
77 print ' CHECKED' if $hashref->{setuptax} eq "Y";
78 print '>';
79
80 print <<END;
81 </TD></TR>
82 <TR><TD ALIGN="right">Recurring fee tax exempt</TD><TD>
83 END
84
85 print '<INPUT TYPE="checkbox" NAME="recurtax" VALUE="Y"';
86 print ' CHECKED' if $hashref->{recurtax} eq "Y";
87 print '>';
88
89 print '</TD></TR>';
90
91 my $conf = new FS::Conf;
92 if ( $conf->exists('enable_taxclasses') ) {
93   print '<TR><TD ALIGN="right">Tax class</TD><TD><SELECT NAME="taxclass">';
94   my $sth = dbh->prepare('SELECT DISTINCT taxclass FROM cust_main_county')
95     or die dbh->errstr;
96   $sth->execute or die $sth->errstr;
97   foreach my $taxclass ( map $_->[0], @{$sth->fetchall_arrayref} ) {
98     print qq!<OPTION VALUE="$taxclass"!;
99     print ' SELECTED' if $taxclass eq $hashref->{taxclass};
100     print qq!>$taxclass</OPTION>!;
101   }
102   print '</SELECT></TD></TR>';
103 } else {
104   print
105     '<INPUT TYPE="hidden" NAME="taxclass" VALUE="'. $hashref->{taxclass}. '">';
106 }
107
108 print '<TR><TD ALIGN="right">Disable new orders</TD><TD>';
109 print '<INPUT TYPE="checkbox" NAME="disabled" VALUE="Y"';
110 print ' CHECKED' if $hashref->{disabled} eq "Y";
111 print '>';
112 print '</TD></TR></TABLE>';
113
114 my $thead =  "\n\n". ntable('#cccccc', 2). <<END;
115 <TR>
116 <TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Quan.</FONT></TH>
117 <TH BGCOLOR="#dcdcdc"><FONT SIZE=-1>Service</FONT></TH>
118 </TR>
119 END
120
121 #unless ( $cgi->param('clone') ) {
122 #dunno why...
123 unless ( 0 ) {
124   #print <<END, $thead;
125   print <<END, itable(), '<TR><TD VALIGN="top">', $thead;
126 <BR><BR>Enter the quantity of each service this package includes.<BR><BR>
127 END
128 }
129
130 my @fixups = ();
131 my $count = 0;
132 my $columns = 3;
133 my @part_svc = qsearch( 'part_svc', { 'disabled' => '' } );
134 foreach my $part_svc ( @part_svc ) {
135   my $svcpart = $part_svc->svcpart;
136   my $pkg_svc = qsearchs( 'pkg_svc', {
137     'pkgpart'  => $cgi->param('clone') || $part_pkg->pkgpart,
138     'svcpart'  => $svcpart,
139   } ) || new FS::pkg_svc ( {
140     'pkgpart'  => $cgi->param('clone') || $part_pkg->pkgpart,
141     'svcpart'  => $svcpart,
142     'quantity' => 0,
143   });
144   #? #next unless $pkg_svc;
145
146   push @fixups, "pkg_svc$svcpart";
147
148   #unless ( defined ($cgi->param('clone')) && $cgi->param('clone') ) {
149   #dunno why...
150   unless ( 0 ) {
151     print '<TR>'; # if $count == 0 ;
152     print qq!<TD><INPUT TYPE="text" NAME="pkg_svc$svcpart" SIZE=4 MAXLENGTH=3 VALUE="!,
153           $cgi->param("pkg_svc$svcpart") || $pkg_svc->quantity || 0,
154           qq!"></TD><TD><A HREF="part_svc.cgi?!,$part_svc->svcpart,
155           qq!">!, $part_svc->getfield('svc'), "</A></TD></TR>";
156 #    print "</TABLE></TD><TD>$thead" if ++$count == int(scalar(@part_svc) / 2);
157     $count+=1;
158     foreach ( 1 .. $columns-1 ) {
159       print "</TABLE></TD><TD VALIGN=\"top\">$thead"
160         if $count == int( $_ * scalar(@part_svc) / $columns );
161     }
162   } else {
163     print qq!<INPUT TYPE="hidden" NAME="pkg_svc$svcpart" VALUE="!,
164           $cgi->param("pkg_svc$svcpart") || $pkg_svc->quantity || 0, qq!">\n!;
165   }
166 }
167
168 #unless ( $cgi->param('clone') ) {
169 #dunno why...
170 unless ( 0 ) {
171   print "</TR></TABLE></TD></TR></TABLE>";
172   #print "</TR></TABLE>";
173 }
174
175 print qq!Default service <SELECT NAME="def_svcpart" onChange="fchanged(this)">!,
176       qq!<OPTION VALUE="0">None!;
177 foreach my $part_svc ( @part_svc ) {
178   print qq!<OPTION VALUE="!, $part_svc->svcpart, '"',
179         (($hashref->{def_svcpart} == $part_svc->svcpart) ? " SELECTED>" : ">"),
180         $part_svc->svc, qq!</OPTION>!;
181 }
182 print qq!</SELECT><BR>\n!;
183
184
185 foreach my $f ( qw( clone pkgnum ) ) {
186   print qq!<INPUT TYPE="hidden" NAME="$f" VALUE="!. $cgi->param($f). '">';
187 }
188 print '<INPUT TYPE="hidden" NAME="pkgpart" VALUE="'. $part_pkg->pkgpart. '">';
189
190 # prolly should be in database
191 tie my %plans, 'Tie::IxHash',
192   'flat' => {
193     'name' => 'Flat rate (anniversary billing)',
194     'fields' => {
195       'setup_fee' => { 'name' => 'Setup fee for this package',
196                        'default' => 0,
197                      },
198       'recur_fee' => { 'name' => 'Recurring fee for this package',
199                        'default' => 0,
200                       },
201     },
202     'fieldorder' => [ 'setup_fee', 'recur_fee' ],
203     'setup' => 'what.setup_fee.value',
204     'recur' => 'what.recur_fee.value',
205   },
206
207   'flat_delayed' => {
208     'name' => 'Free for X days, then flat rate (anniversary billing)',
209     'fields' =>  {
210       'free_days' => { 'name' => 'Initial free days',
211                        'default' => 0,
212                      },
213       'setup_fee' => { 'name' => 'Setup fee for this package',
214                        'default' => 0,
215                      },
216       'recur_fee' => { 'name' => 'Recurring fee for this package',
217                        'default' => 0,
218                       },
219     },
220     'fieldorder' => [ 'free_days', 'setup_fee', 'recur_fee' ],
221     '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',
222     'recur' => 'what.recur_fee.value',
223   },
224
225   'prorate' => {
226     'name' => 'First partial month pro-rated, then flat-rate (1st of month billing)',
227     'fields' =>  {
228       'setup_fee' => { 'name' => 'Setup fee for this package',
229                        'default' => 0,
230                      },
231       'recur_fee' => { 'name' => 'Recurring fee for this package',
232                        'default' => 0,
233                       },
234     },
235     'fieldorder' => [ 'setup_fee', 'recur_fee' ],
236     'setup' => 'what.setup_fee.value',
237     '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) ; \'',
238   },
239
240   'subscription' => {
241     'name' => 'First partial month full charge, then flat-rate (1st of month billing)',
242     'fields' => {
243       'setup_fee' => { 'name' => 'Setup fee for this package',
244                        'default' => 0,
245                      },
246       'recur_fee' => { 'name' => 'Recurring fee for this package',
247                        'default' => 0,
248                       },
249     },
250     'fieldorder' => [ 'setup_fee', 'recur_fee' ],
251     'setup' => 'what.setup_fee.value',
252     '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',
253   },
254
255   'flat_comission_cust' => {
256     'name' => 'Flat rate with recurring comission per active customer',
257     'fields' => {
258       'setup_fee' => { 'name' => 'Setup fee for this package',
259                        'default' => 0,
260                      },
261       'recur_fee' => { 'name' => 'Recurring fee for this package',
262                        'default' => 0,
263                      },
264       'comission_amount' => { 'name' => 'Comission amount per month (per active customer)',
265                               'default' => 0,
266                             },
267       'comission_depth'  => { 'name' => 'Number of layers',
268                               'default' => 1,
269                             },
270     },
271     'fieldorder' => [ 'setup_fee', 'recur_fee', 'comission_depth', 'comission_amount' ],
272     'setup' => 'what.setup_fee.value',
273     '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 + \';\'',
274   },
275
276   'flat_comission' => {
277     'name' => 'Flat rate with recurring comission per (any) active package',
278     'fields' => {
279       'setup_fee' => { 'name' => 'Setup fee for this package',
280                        'default' => 0,
281                      },
282       'recur_fee' => { 'name' => 'Recurring fee for this package',
283                        'default' => 0,
284                      },
285       'comission_amount' => { 'name' => 'Comission amount per month (per active package)',
286                               'default' => 0,
287                             },
288       'comission_depth'  => { 'name' => 'Number of layers',
289                               'default' => 1,
290                             },
291     },
292     'fieldorder' => [ 'setup_fee', 'recur_fee', 'comission_depth', 'comission_amount' ],
293     'setup' => 'what.setup_fee.value',
294     '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 + \';\'',
295   },
296
297   'flat_comission_pkg' => {
298     'name' => 'Flat rate with recurring comission per (selected) active package',
299     'fields' => {
300       'setup_fee' => { 'name' => 'Setup fee for this package',
301                        'default' => 0,
302                      },
303       'recur_fee' => { 'name' => 'Recurring fee for this package',
304                        'default' => 0,
305                      },
306       'comission_amount' => { 'name' => 'Comission amount per month (per uncancelled package)',
307                               'default' => 0,
308                             },
309       'comission_depth'  => { 'name' => 'Number of layers',
310                               'default' => 1,
311                             },
312       'comission_pkgpart' => { 'name' => 'Applicable packages<BR><FONT SIZE="-1">(hold <b>ctrl</b> to select multiple packages)</FONT>',
313                                'type' => 'select_multiple',
314                                'select_table' => 'part_pkg',
315                                'select_hash'  => { 'disabled' => '' } ,
316                                'select_key'   => 'pkgpart',
317                                'select_label' => 'pkg',
318                              },
319     },
320     'fieldorder' => [ 'setup_fee', 'recur_fee', 'comission_depth', 'comission_amount', 'comission_pkgpart' ],
321     'setup' => 'what.setup_fee.value',
322     '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 + \';\'',
323   },
324
325
326
327   'sesmon_hour' => {
328     'name' => 'Base charge plus charge per-hour from the session monitor',
329     'fields' => {
330       'setup_fee' => { 'name' => 'Setup fee for this package',
331                        'default' => 0,
332                      },
333       'recur_flat' => { 'name' => 'Base monthly charge for this package',
334                         'default' => 0,
335                       },
336       'recur_included_hours' => { 'name' => 'Hours included',
337                                   'default' => 0,
338                                 },
339       'recur_hourly_charge' => { 'name' => 'Additional charge per hour',
340                                  'default' => 0,
341                                },
342     },
343     'fieldorder' => [ 'setup_fee', 'recur_flat', 'recur_included_hours', 'recur_hourly_charge' ],
344     'setup' => 'what.setup_fee.value',
345     '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;\'',
346   },
347
348   'sesmon_minute' => {
349     'name' => 'Base charge plus charge per-minute from the session monitor',
350     'fields' => {
351       'setup_fee' => { 'name' => 'Setup fee for this package',
352                        'default' => 0,
353                      },
354       'recur_flat' => { 'name' => 'Base monthly charge for this package',
355                         'default' => 0,
356                       },
357       'recur_included_min' => { 'name' => 'Minutes included',
358                                 'default' => 0,
359                                 },
360       'recur_minly_charge' => { 'name' => 'Additional charge per minute',
361                                 'default' => 0,
362                               },
363     },
364     'fieldorder' => [ 'setup_fee', 'recur_flat', 'recur_included_min', 'recur_minly_charge' ],
365     'setup' => 'what.setup_fee.value',
366     '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;\'',
367
368   },
369
370 ;
371
372 my %plandata = map { /^(\w+)=(.*)$/; ( $1 => $2 ); }
373                     split("\n", $part_pkg->plandata );
374
375 tie my %options, 'Tie::IxHash', map { $_=>$plans{$_}->{'name'} } keys %plans;
376
377 my @form_select = ();
378 if ( $conf->exists('enable_taxclasses') ) {
379   push @form_select, 'taxclass';
380 } else {
381   push @fixups, 'taxclass'; #hidden
382 }
383
384
385 my $widget = new HTML::Widgets::SelectLayers(
386   'selected_layer' => $part_pkg->plan,
387   'options'        => \%options,
388   'form_name'      => 'dummy',
389   'form_action'    => 'process/part_pkg.cgi',
390   'form_text'      => [ qw(pkg comment freq clone pkgnum pkgpart), @fixups ],
391   'form_checkbox'  => [ qw(setuptax recurtax disabled) ],
392   'form_select'    => [ qw(def_svcpart), @form_select ],
393   'fixup_callback' => sub {
394                         #my $ = @_;
395                         my $html = '';
396                         for my $p ( keys %plans ) {
397                           $html .= "if ( what.plan.value == \"$p\" ) {
398                                       what.setup.value = $plans{$p}->{setup} ;
399                                       what.recur.value = $plans{$p}->{recur} ;
400                                     }\n";
401                         }
402                         $html;
403                       },
404   'layer_callback' => sub {
405     my $layer = shift;
406     my $html = qq!<INPUT TYPE="hidden" NAME="plan" VALUE="$layer">!.
407                ntable("#cccccc",2);
408     my $href = $plans{$layer}->{'fields'};
409     foreach my $field ( exists($plans{$layer}->{'fieldorder'})
410                           ? @{$plans{$layer}->{'fieldorder'}}
411                           : keys %{ $href }
412                       ) {
413
414       $html .= '<TR><TD ALIGN="right">'. $href->{$field}{'name'}. '</TD><TD>';
415
416       if ( ! exists($href->{$field}{'type'}) ) {
417         $html .= qq!<INPUT TYPE="text" NAME="$field" VALUE="!.
418                  ( exists($plandata{$field})
419                      ? $plandata{$field}
420                      : $href->{$field}{'default'} ).
421                  qq!" onChange="fchanged(this)">!;
422       } elsif ( $href->{$field}{'type'} eq 'select_multiple' ) {
423         $html .= qq!<SELECT MULTIPLE NAME="$field" onChange="fchanged(this)">!;
424         foreach my $record (
425           qsearch( $href->{$field}{'select_table'},
426                    $href->{$field}{'select_hash'}   )
427         ) {
428           my $value = $record->getfield($href->{$field}{'select_key'});
429           $html .= qq!<OPTION VALUE="$value"!.
430                    (  $plandata{$field} =~ /(^|, *)$value *(,|$)/
431                         ? ' SELECTED'
432                         : ''          ).
433                    '>'. $record->getfield($href->{$field}{'select_label'})
434         }
435         $html .= '</SELECT>';
436       }
437
438       $html .= '</TD></TR>';
439     }
440     $html .= '</TABLE>';
441
442     $html .= '<INPUT TYPE="hidden" NAME="plandata" VALUE="'.
443              join(',', keys %{ $href } ). '">'.
444              '<BR><BR>';
445              
446     $html .= '<INPUT TYPE="submit" VALUE="'.
447              ( $hashref->{pkgpart} ? "Apply changes" : "Add package" ).
448              '" onClick="fchanged(this)">';
449
450     $html .= '<BR><BR>don\'t edit this unless you know what you\'re doing '.
451              '<INPUT TYPE="button" VALUE="refresh expressions" '.
452                'onClick="fchanged(this)">'.
453              ntable("#cccccc",2).
454              '<TR><TD>'.
455              '<FONT SIZE="1">Setup expression<BR>'.
456              '<INPUT TYPE="text" NAME="setup" SIZE="160" VALUE="'.
457                $hashref->{setup}. '" onLoad="fchanged(this)">'.
458              '</FONT><BR>'.
459              '<FONT SIZE="1">Recurring espression<BR>'.
460              '<INPUT TYPE="text" NAME="recur" SIZE="160" VALUE="'.
461                $hashref->{recur}. '" onLoad="fchanged(this)">'.
462              '</FONT>'.
463              '</TR></TD>'.
464              '</TABLE>';
465
466     $html;
467
468   },
469 );
470
471 %>
472
473 <BR>
474 Price plan <%= $widget->html %>
475   </BODY>
476 </HTML>