first part of ACL and re-skinning work and some other small stuff
[freeside.git] / httemplate / view / cust_main / packages.html
1 <%
2   my( $cust_main ) = @_;
3   my $conf = new FS::Conf;
4
5   my $packages = get_packages($cust_main, $conf);
6 %>
7
8 <STYLE TYPE="text/css">
9 .package .provision { font-weight: bold }
10 </STYLE>
11
12 <A NAME="cust_pkg"><FONT SIZE="+2">Packages</FONT></A>
13
14 <%= include('order_pkg.html', $cust_main ) %>
15
16 <% if ( $conf->config('payby-default') ne 'HIDE' ) { %>
17   <%= include('quick-charge.html', $cust_main ) %>
18 <% } %>
19
20 <A HREF="<%= $p %>edit/cust_pkg.cgi?<%= $cust_main->custnum %>">Bulk order and cancel packages</A> (preserves services)
21 <BR><BR>
22
23 <% if ( @$packages ) { %>
24 Current packages
25 <% } %>
26
27 <% if ( $cust_main->num_cancelled_pkgs ) {
28      if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me
29           || ( $conf->exists('hidecancelledpackages')
30                && ! $cgi->param('showcancelledpackages')
31              )
32         )
33      {
34        $cgi->param('showcancelledpackages', 1);
35 %>
36   ( <a href="<%= $cgi->self_url %>">show
37 <%   } else {
38        $cgi->param('showcancelledpackages', 0);
39 %>
40   ( <a href="<%= $cgi->self_url %>">hide
41 <%   } %>
42  cancelled packages</a> )
43 <% } %>
44
45 <% if ( @$packages ) { %>
46
47 <TABLE CLASS="package" BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999">
48 <TR>
49   <TH>Package</TH>
50   <TH>Status</TH>
51   <TH COLSPAN=2>Services</TH>
52 </TR>
53
54 <%
55 foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) {
56   my $rowspan = 0;
57
58   if ($pkg->{cancel}) {
59     $rowspan = 0;
60   } else {
61     foreach my $svcpart (@{$pkg->{svcparts}}) {
62       $rowspan += $svcpart->{count};
63       $rowspan++ if ($svcpart->{count} < $svcpart->{quantity});
64     }
65   } 
66 %>
67
68 <!--pkgnum: <%=$pkg->{pkgnum}%>-->
69 <TR>
70   <TD ROWSPAN=<%= $rowspan || 1 %>>
71     <A NAME="cust_pkg<%=$pkg->{pkgnum}%>"><%=$pkg->{pkgnum}%></A>:
72     <%=$pkg->{pkg}%> - <%=$pkg->{comment}%><BR>
73 <% unless ($pkg->{cancel}) { %>
74     (&nbsp;<%=pkg_change_link($pkg)%>&nbsp;)
75     (&nbsp;<%=pkg_dates_link($pkg)%>&nbsp;|&nbsp;<%=pkg_customize_link($pkg,$cust_main->custnum)%>&nbsp;)
76 <% } %>
77   </TD>
78   <TD ROWSPAN=<%= $rowspan || 1 %>>
79     <TABLE BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
80
81 <%
82   sub myfreq {
83     my $part_pkg = shift;
84     my $freq = $part_pkg->freq_pretty;
85     $freq =~ s/ /&nbsp;/g;
86     $freq;
87   }
88
89   #this should use cust_pkg->status and cust_pkg->statuscolor eventually
90
91   my $colspan = $conf->exists('cust_pkg-display_times') ? 8 : 4;
92   my $width = $conf->exists('cust_pkg-display_times') ? '38%' : '56%';
93
94   #false laziness w/edit/REAL_cust_pkg.cgi
95   my( $billed_or_prepaid, $last_bill_or_renewed, $next_bill_or_prepaid_until );
96   unless ( $pkg->{'part_pkg'}->is_prepaid ) {
97     $billed_or_prepaid = 'billed';
98     $last_bill_or_renewed = 'Last&nbsp;bill';
99     $next_bill_or_prepaid_until = 'Next&nbsp;bill';
100   } else {
101     $billed_or_prepaid = 'prepaid';
102     $last_bill_or_renewed = 'Renewed';
103     $next_bill_or_prepaid_until = 'Prepaid&nbsp;until';
104   }
105
106 %>
107
108 <% if ( $pkg->{cancel} ) { %> <!-- #status: cancelled -->
109
110   <TR>
111     <TD WIDTH="<%=$width%>" ALIGN="right"><FONT COLOR="#ff0000"><B>Cancelled&nbsp;</B></FONT></TD>
112     <%= pkg_datestr($pkg,'cancel',$conf) %>
113   </TR>
114
115   <% unless ( $pkg->{setup} ) { %>
116
117     <TR>
118       <TD COLSPAN=<%=$colspan%>>Never billed</TD>
119     </TR>
120
121   <% } else { %>
122
123     <TR>
124       <TD WIDTH="<%=$width%>" ALIGN="right">Setup&nbsp;</TD>
125       <%= pkg_datestr($pkg, 'setup',$conf) %>
126     </TR>
127
128     <% if ( $pkg->{'last_bill'} ) { %>
129       <TR>
130         <TD WIDTH="<%=$width%>" ALIGN="right"><%= $last_bill_or_renewed %>&nbsp;</TD>
131         <%= pkg_datestr($pkg, 'last_bill',$conf) %>
132       </TR>
133     <% } %>
134
135     <% if ( $pkg->{'susp'} ) { %>
136       <TR>
137         <TD WIDTH="<%=$width%>" ALIGN="right">Suspended&nbsp;</TD>
138         <%= pkg_datestr($pkg, 'susp',$conf) %>
139       </TR>
140     <% } %>
141
142   <% } %>
143
144 <% } else { %>
145
146   <% if ( $pkg->{susp} ) { %> <!-- #status: suspended -->
147
148     <TR>
149       <TD WIDTH="<%=$width%>" ALIGN="right"><FONT COLOR="#FF9900"><B>Suspended</B>&nbsp;</FONT></TD>
150       <%= pkg_datestr($pkg,'susp',$conf) %>
151     </TR>
152
153     <% unless ( $pkg->{setup} ) { %>
154
155       <TR>
156         <TD COLSPAN=<%=$colspan%>>Never billed</TD>
157       </TR>
158
159     <% } else { %>
160
161       <TR>
162         <TD WIDTH="<%=$width%>" ALIGN="right">Setup&nbsp;</TD>
163         <%= pkg_datestr($pkg, 'setup',$conf) %>
164       </TR>
165       
166     <% } %>
167
168     <% if ( $pkg->{'last_bill'} ) { %>
169       <TR>
170         <TD WIDTH="<%=$width%>" ALIGN="right"><%= $last_bill_or_renewed %>&nbsp;</TD>
171         <%= pkg_datestr($pkg, 'last_bill',$conf) %>
172       </TR>
173     <% } %>
174
175     <!-- # next bill ?? -->
176
177     <% if ( $pkg->{'expire'} ) { %>
178       <TR>
179         <TD WIDTH="<%=$width%>" ALIGN="right">Expires&nbsp;</TD>
180         <%= pkg_datestr($pkg, 'expire',$conf) %>
181       </TR>
182     <% } %>
183
184     <TR>
185       <TD COLSPAN=<%=$colspan%>>(&nbsp;<%= pkg_unsuspend_link($pkg) %>&nbsp;|&nbsp;<%= pkg_cancel_link($pkg) %>&nbsp;)</TD>
186     </TR>
187
188   <% } else { %> <!-- #status: active -->
189
190     <% unless ( $pkg->{setup} ) { %> <!-- #not setup -->
191
192       <% unless ( $pkg->{'freq'} ) { %>
193
194         <TR>
195           <TD COLSPAN=<%=$colspan%>>Not&nbsp;yet&nbsp;billed&nbsp;(one-time&nbsp;charge)</TD>
196         </TR>
197
198         <TR>
199           <TD COLSPAN=<%=$colspan%>>(&nbsp;<%= pkg_cancel_link($pkg) %>&nbsp;)</TD>
200         </TR>
201
202       <% } else { %>
203
204         <TR>
205           <TD COLSPAN=<%=$colspan%>>Not&nbsp;yet&nbsp;billed&nbsp;(<%= $billed_or_prepaid %>&nbsp;<%= myfreq($pkg->{part_pkg}) %>)</TD>
206         </TR>
207
208       <% } %>
209
210     <% } else { %> <!-- #setup -->
211
212       <% unless ( $pkg->{freq} ) { %>
213
214         <TR>
215           <TD COLSPAN=<%=$colspan%>>One-time&nbsp;charge</TD>
216         </TR>
217
218         <TR>
219           <TD WIDTH="<%=$width%>" ALIGN="right">Billed&nbsp;</TD>
220           <%= pkg_datestr($pkg,'setup',$conf) %>
221         </TR>
222
223       <% } else { %>
224
225         <TR>
226           <TD COLSPAN=<%=$colspan%>><FONT COLOR="#00CC00"><B>Active</B></FONT>,&nbsp;<%= $billed_or_prepaid %>&nbsp;<%= myfreq($pkg->{part_pkg}) %></TD>
227         </TR>
228
229         <TR>
230           <TD WIDTH="<%=$width%>" ALIGN="right">Setup&nbsp;</TD>
231           <%= pkg_datestr($pkg, 'setup',$conf) %>
232         </TR>
233
234       <% } %>
235
236     <% } %>
237
238     <% if ( $pkg->{'last_bill'} ) { %>
239       <TR>
240         <TD WIDTH="<%=$width%>" ALIGN="right"><%= $last_bill_or_renewed %>&nbsp;</TD>
241         <%= pkg_datestr($pkg, 'last_bill',$conf) %>
242       </TR>
243     <% } %>
244
245     <% if ( $pkg->{'next_bill'} ) { %>
246       <TR>
247         <TD WIDTH="<%=$width%>" ALIGN="right"><%= $next_bill_or_prepaid_until %>&nbsp;</TD>
248         <%= pkg_datestr($pkg, 'next_bill',$conf) %>
249       </TR>
250     <% } %>
251
252     <% if ( $pkg->{'expire'} ) { %>
253       <TR>
254         <TD WIDTH="<%=$width%>" ALIGN="right">Expires&nbsp;</TD>
255         <%= pkg_datestr($pkg, 'expire',$conf) %>
256       </TR>
257     <% } %>
258
259     <% if ( $pkg->{freq} ) { %>
260       <TR>
261         <TD COLSPAN=<%=$colspan%>>(&nbsp;<%= pkg_suspend_link($pkg) %>&nbsp;|&nbsp;<%= pkg_cancel_link($pkg) %>&nbsp;)</TD>
262       </TR>
263     <% } %>
264
265   <% } %>
266
267 <% } %>
268
269 </TABLE>
270 </TD>
271
272 <%
273   if ($rowspan == 0) { print qq!</TR>\n!; next; }
274
275   my $cnt = 0;
276   foreach my $svcpart (sort {$a->{svcpart} <=> $b->{svcpart}} @{$pkg->{svcparts}}) {
277     foreach my $service (@{$svcpart->{services}}) {
278       print '<TR>' if ($cnt > 0);
279 %>
280   <TD><%=svc_link($svcpart,$service)%></TD>
281   <TD><%=svc_label_link($svcpart,$service)%><BR>(&nbsp;<%=svc_unprovision_link($service)%>&nbsp;)</TD>
282 </TR>
283 <%
284       $cnt++;
285     }
286     if ($svcpart->{count} < $svcpart->{quantity}) {
287       print qq!<TR>\n! if ($cnt > 0);
288       print qq!  <TD COLSPAN=2>!.svc_provision_link($pkg, $svcpart, $conf).qq!</TD>\n</TR>\n!;
289     }
290   }
291 }
292 #end display packages
293 %>
294
295 </TABLE>
296 <% } else { %>
297 <BR>
298 <% } %>
299
300 <%
301 #subroutines
302
303 sub get_packages {
304   my $cust_main = shift or return undef;
305   my $conf = shift;
306   
307   my @packages = ();
308   my $method;
309   if (  $cgi->param('showcancelledpackages') eq '0' #see if it was set by me
310      || ( $conf->exists('hidecancelledpackages')
311            && ! $cgi->param('showcancelledpackages') )
312      )
313   {
314     $method = 'ncancelled_pkgs';
315   } else {
316     $method = 'all_pkgs';
317   }
318   
319   foreach my $cust_pkg ( $cust_main->$method() ) {
320   
321     my $part_pkg = $cust_pkg->part_pkg;
322
323     my %pkg = ();
324
325     #to get back to the original object... should use it in the first place!!
326     $pkg{cust_pkg} = $cust_pkg;
327     $pkg{part_pkg} = $part_pkg;
328
329     $pkg{pkgnum} = $cust_pkg->pkgnum;
330     $pkg{pkg} = $part_pkg->pkg;
331     $pkg{pkgpart} = $part_pkg->pkgpart;
332     $pkg{comment} = $part_pkg->getfield('comment');
333     $pkg{freq} = $part_pkg->freq;
334     $pkg{setup} = $cust_pkg->getfield('setup');
335     $pkg{last_bill} = $cust_pkg->getfield('last_bill');
336     $pkg{next_bill} = $cust_pkg->getfield('bill');
337     $pkg{susp} = $cust_pkg->getfield('susp');
338     $pkg{expire} = $cust_pkg->getfield('expire');
339     $pkg{cancel} = $cust_pkg->getfield('cancel');
340
341   
342     my %svcparts = map {
343       $_->svcpart => {
344                        $_->part_svc->hash,
345                        'quantity' => $_->quantity,
346                        'count'    => $cust_pkg->num_cust_svc($_->svcpart),
347                        #'services' => [],
348                      };
349     } $part_pkg->pkg_svc;
350
351     foreach my $cust_svc ( $cust_pkg->cust_svc ) {
352       #warn "svcnum ". $cust_svc->svcnum. " / svcpart ". $cust_svc->svcpart. "\n";
353       my $svc = {
354         'svcnum' => $cust_svc->svcnum,
355         'label'  => ($cust_svc->label)[1],
356       };
357
358       #false laziness with above, to catch extraneous services.  whole
359       #damn thing should be OO...
360       my $svcpart = ( $svcparts{$cust_svc->svcpart} ||= {
361         $cust_svc->part_svc->hash,
362         'quantity' => 0,
363         'count'    => $cust_pkg->num_cust_svc($cust_svc->svcpart),
364         #'services' => [],
365       } );
366
367       push @{$svcpart->{services}}, $svc;
368
369     }
370
371     $pkg{svcparts} = [ values %svcparts ];
372
373     push @packages, \%pkg;
374   
375   }
376   
377   return \@packages;
378
379 }
380
381 sub svc_link {
382
383   my ($svcpart, $svc) = (shift,shift) or return '';
384   return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svcpart->{svc}</A>!;
385
386 }
387
388 sub svc_label_link {
389
390   my ($svcpart, $svc) = (shift,shift) or return '';
391   return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svc->{label}</A>!;
392
393 }
394
395 sub svc_provision_link {
396   my ($pkg, $svcpart, $conf) = @_;
397   ( my $svc_nbsp = $svcpart->{svc} ) =~ s/\s+/&nbsp;/g;
398   my $num_left = $svcpart->{quantity} - $svcpart->{count};
399   my $pkgnum_svcpart = "pkgnum$pkg->{pkgnum}-svcpart$svcpart->{svcpart}";
400
401   my $url;
402   if ( $svcpart->{svcdb} eq 'svc_external'
403        && $conf->exists('svc_external-skip_manual')
404   ) {
405     $url = "${p}edit/process/$svcpart->{svcdb}.cgi?".
406            "pkgnum=$pkg->{pkgnum}&".
407            "svcpart=$svcpart->{svcpart}";
408   } else {
409     $url = "${p}edit/$svcpart->{svcdb}.cgi?$pkgnum_svcpart";
410   }
411
412   my $link = qq!<A CLASS="provision" HREF="$url">!.
413              "Provision&nbsp;$svc_nbsp&nbsp;($num_left)</A>";
414   if ( $conf->exists('legacy_link') ) {
415     $link .= '<BR>'.
416              qq!<A CLASS="provision" HREF="${p}misc/link.cgi?!.
417              qq!$pkgnum_svcpart">!.
418             "Link&nbsp;to&nbsp;legacy&nbsp;$svc_nbsp&nbsp;($num_left)</A>";
419   }
420   $link;
421 }
422
423 sub svc_unprovision_link {
424   my $svc = shift or return '';
425   qq!<A HREF="javascript:areyousure('${p}misc/unprovision.cgi?$svc->{svcnum}',!.
426   qq!'Permanently unprovision and delete this service?')">Unprovision</A>!;
427 }
428
429 # This should be generalized to use config options to determine order.
430 sub pkgsort_pkgnum_cancel {
431   if ($a->{cancel} and $b->{cancel}) {
432     return ($a->{pkgnum} <=> $b->{pkgnum});
433   } elsif ($a->{cancel} or $b->{cancel}) {
434     return (-1) if ($b->{cancel});
435     return (1) if ($a->{cancel});
436     return (0);
437   } else {
438     return($a->{pkgnum} <=> $b->{pkgnum});
439   }
440 }
441
442 sub pkg_datestr {
443   my($pkg, $field, $conf) = @_ or return '';
444   return '&nbsp;' unless $pkg->{$field};
445   my $format = '<TD align="left"><B>%b</B></TD>'.
446                '<TD align="right"><B>&nbsp;%o,</B></TD>'.
447                '<TD align="right"><B>&nbsp;%Y</B></TD>';
448   #$format .= '&nbsp;<FONT SIZE=-3>%l:%M:%S%P&nbsp;%z</FONT>'
449   $format .= '<TD ALIGN="right"><B>&nbsp;%l</TD>'.
450              '<TD ALIGN="center"><B>:</B></TD>'.
451              '<TD ALIGN="left"><B>%M</B></TD>'.
452              '<TD ALIGN="left"><B>&nbsp;%P</B></TD>'
453     if $conf->exists('cust_pkg-display_times');
454   ( my $strip = time2str($format, $pkg->{$field}) ) =~ s/ (\d)/$1/g;
455   $strip;
456 }
457
458 sub pkg_change_link {
459   my $pkg = shift or return '';
460   return qq!<a href="${p}misc/change_pkg.cgi?$pkg->{pkgnum}">!.
461          qq!Change&nbsp;package</a>!;
462 }
463
464 sub pkg_suspend_link {
465   my $pkg = shift or return '';
466   return qq!<a href="${p}misc/susp_pkg.cgi?$pkg->{pkgnum}">Suspend</a>!;
467 }
468
469 sub pkg_unsuspend_link {
470   my $pkg = shift or return '';
471   return qq!<a href="${p}misc/unsusp_pkg.cgi?$pkg->{pkgnum}">Unsuspend</a>!;
472 }
473
474 sub pkg_cancel_link {
475   my $pkg = shift or return '';
476   qq!<A HREF="javascript:areyousure('${p}misc/cancel_pkg.cgi?$pkg->{pkgnum}', !.
477   qq!'Permanently delete included services and cancel this package?')">!.
478   qq!Cancel now</A> | !.
479   qq!<A HREF="${p}misc/expire_pkg.cgi?$pkg->{pkgnum}">Cancel later</A>!;
480 }
481
482 sub pkg_dates_link {
483   my $pkg = shift or return '';
484   qq!<A HREF="${p}edit/REAL_cust_pkg.cgi?$pkg->{pkgnum}">Edit&nbsp;dates</A>!;
485 }
486
487 sub pkg_customize_link {
488   my $pkg = shift or return '';
489   my $custnum = shift;
490   qq!<A HREF="${p}edit/part_pkg.cgi?keywords=$custnum;clone=$pkg->{pkgpart};!.
491   qq!pkgnum=$pkg->{pkgnum}">Customize</A>!;
492 }
493
494 %>