1 <TD CLASS="inv" BGCOLOR="<% $bgcolor %>" VALIGN="top">
2 <TABLE CLASS="inv" BORDER=0 CELLSPACING=0 CELLPADDING=0 WIDTH="100%">
4 %#this should use cust_pkg->status and cust_pkg->statuscolor eventually
6 % if ( $supplemental ) {
7 <% pkg_status_row_colspan($cust_pkg, emt('Supplemental'), '', 'color' => '7777FF', %opt) %>
8 % } elsif ( $cust_pkg->order_date ) {
9 <% pkg_status_row($cust_pkg, emt('Ordered'), 'order_date', %opt ) %>
12 % if ( $cust_pkg->get('cancel') ) { #status: cancelled
13 % my $cpr = $cust_pkg->last_cust_pkg_reason('cancel');
15 <% pkg_status_row($cust_pkg, emt('Cancelled'), 'cancel', 'color'=>'FF0000', %opt ) %>
17 <% pkg_reason_row($cust_pkg, $cpr, color => 'ff0000', %opt) %>
19 % unless ( $cust_pkg->get('setup') ) {
21 <% pkg_status_row_colspan( $cust_pkg, emt('Never billed'), '', %opt, ) %>
25 <% pkg_status_row( $cust_pkg, emt('Setup'), 'setup', %opt ) %>
26 <% pkg_status_row_changed( $cust_pkg, %opt ) %>
27 <% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
28 <% pkg_status_row_if( $cust_pkg, emt('Suspended'), 'susp', %opt, curuser=>$curuser ) %>
32 % if ( $part_pkg->freq and !$supplemental ) { #?
35 <TD COLSPAN=<%$opt{colspan}%>>
37 % if ( $curuser->access_right('Un-cancel customer package') ) {
38 ( <% pkg_uncancel_link($cust_pkg) %> )
47 % if ( $cust_pkg->get('susp') ) { #status: suspended
48 % my $cpr = $cust_pkg->last_cust_pkg_reason('susp');
50 <% pkg_status_row( $cust_pkg, emt('Suspended'), 'susp', 'color'=>'FF9900', %opt ) %>
52 <% pkg_reason_row( $cust_pkg, $cpr, 'color' => 'FF9900', %opt ) %>
54 <% pkg_status_row_noauto( $cust_pkg, %opt ) %>
56 <% pkg_status_row_discount( $cust_pkg, %opt ) %>
58 % unless ( $cust_pkg->get('setup') ) {
59 <% pkg_status_row_colspan( $cust_pkg, emt('Never billed'), '', %opt ) %>
61 <% pkg_status_row($cust_pkg, emt('Setup'), 'setup', %opt ) %>
64 <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
66 <% pkg_status_row_changed( $cust_pkg, %opt ) %>
67 <% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
68 % if ( $cust_pkg->option('suspend_bill', 1)
69 % || ( $part_pkg->option('suspend_bill', 1)
70 % && ! $cust_pkg->option('no_suspend_bill',1)
74 <% pkg_status_row_if( $cust_pkg, emt('Next bill'), 'bill', %opt, curuser=>$curuser ) %>
76 <% pkg_status_row_if( $cust_pkg, emt('Will resume'), 'resume', %opt, curuser=>$curuser ) %>
77 <% pkg_status_row_if( $cust_pkg, emt('Expires'), 'expire', %opt, curuser=>$curuser ) %>
78 <% pkg_status_row_if( $cust_pkg, emt('Contract ends'), 'contract_end', %opt ) %>
80 % if ( !$supplemental ) {
82 <TD COLSPAN=<%$opt{colspan}%>>
84 % if ( $curuser->access_right('Unsuspend customer package') ) {
85 ( <% pkg_unsuspend_link($cust_pkg) %> )
86 ( <% pkg_resume_link($cust_pkg) %> )
88 % if ( $curuser->access_right('Cancel customer package immediately') ) {
89 ( <% pkg_cancel_link($cust_pkg) %> )
96 % } else { #status: active
98 % unless ( $cust_pkg->get('setup') ) { #not setup
100 % unless ( $part_pkg->freq ) {
102 <% pkg_status_row_colspan( $cust_pkg, emt('Not yet billed (one-time charge)'), '', %opt ) %>
104 <% pkg_status_row_noauto( $cust_pkg, %opt ) %>
106 <% pkg_status_row_discount( $cust_pkg, %opt ) %>
108 <% pkg_status_row_if(
110 ( $part_pkg->freq ? emt('Start billing') : emt('Bill on') ),
116 <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
118 % if (!$supplemental) {
120 <TD COLSPAN=<%$opt{colspan}%>>
122 % if ( $curuser->access_right('Cancel customer package immediately') ) {
123 ( <% pkg_cancel_link($cust_pkg) %> )
132 <% pkg_status_row_colspan($cust_pkg, emt("Not yet billed ($billed_or_prepaid [_1])", myfreq($part_pkg) ), '', %opt ) %>
134 <% pkg_status_row_noauto( $cust_pkg, %opt ) %>
136 <% pkg_status_row_discount( $cust_pkg, %opt ) %>
138 <% pkg_status_row_if($cust_pkg, emt('Start billing'), 'start_date', %opt) %>
139 <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
145 % unless ( $part_pkg->freq ) {
147 <% pkg_status_row_colspan($cust_pkg, emt('One-time charge'), '', %opt ) %>
149 <% pkg_status_row($cust_pkg, emt('Billed'), 'setup', %opt) %>
151 <% pkg_status_row_noauto( $cust_pkg, %opt ) %>
153 <% pkg_status_row_discount( $cust_pkg, %opt ) %>
155 <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
159 % my $num_cust_svc = $cust_pkg->num_cust_svc;
160 % my $summarize = $opt{'cust_pkg-large_pkg_size'} > 0
161 % && $opt{'cust_pkg-large_pkg_size'} <= $num_cust_svc;
163 % #overlimit process is expensive with many services, so skip w/summary
164 % if ( !$summarize && scalar($cust_pkg->overlimit) ) {
166 <% pkg_status_row_colspan( $cust_pkg,
168 $billed_or_prepaid. ' '. myfreq($part_pkg),
175 <% pkg_status_row_colspan( $cust_pkg,
177 $billed_or_prepaid. ' '. myfreq($part_pkg),
184 <% pkg_status_row_noauto( $cust_pkg, %opt ) %>
186 <% pkg_status_row_discount( $cust_pkg, %opt ) %>
188 <% pkg_status_row($cust_pkg, emt('Setup'), 'setup', %opt) %>
190 <% pkg_status_row_if($cust_pkg, emt('Un-cancelled'), 'uncancel', %opt ) %>
196 % if ( $opt{'cust_pkg-show_autosuspend'} ) {
197 % my $autosuspend = pkg_autosuspend_time( $cust_pkg );
198 % $cust_pkg->set('autosuspend', $autosuspend) if $autosuspend;
201 <% pkg_status_row_changed( $cust_pkg, %opt ) %>
202 <% pkg_status_row_if( $cust_pkg, $last_bill_or_renewed, 'last_bill', %opt, curuser=>$curuser ) %>
203 <% pkg_status_row_if( $cust_pkg, $next_bill_or_prepaid_until, 'bill', %opt, curuser=>$curuser ) %>
204 <% pkg_status_row_if($cust_pkg, emt('Will automatically suspend by'), 'autosuspend', %opt) %>
205 <% pkg_status_row_if($cust_pkg, emt('Automatic suspension delayed until'), 'dundate', %opt) %>
206 <% pkg_status_row_if( $cust_pkg, emt('Will suspend on'), 'adjourn', %opt, curuser=>$curuser ) %>
207 <% pkg_status_row_if( $cust_pkg, emt('Will resume on'), 'resume', %opt, curuser=>$curuser ) %>
208 <% pkg_status_row_if( $cust_pkg, emt('Expires'), 'expire', %opt, curuser=>$curuser ) %>
209 <% pkg_status_row_if( $cust_pkg, emt('Contract ends'), 'contract_end', %opt ) %>
211 % if ( $part_pkg->freq and !$supplemental ) {
214 <TD COLSPAN=<%$opt{colspan}%>>
216 % if ( $curuser->access_right('Suspend customer package') ) {
217 ( <% pkg_suspend_link($cust_pkg) %> )
219 % if ( $curuser->access_right('Suspend customer package later') ) {
220 ( <% pkg_adjourn_link($cust_pkg) %> )
222 % if ( $curuser->access_right('Delay suspension events') ) {
223 ( <% pkg_delay_link($cust_pkg) %> )
225 % if ( $curuser->access_right('Cancel customer package immediately') ) {
226 ( <% pkg_cancel_link($cust_pkg) %> )
228 % if ( $curuser->access_right('Cancel customer package later') ) {
229 ( <% pkg_expire_link($cust_pkg) %> )
246 my $bgcolor = $opt{'bgcolor'};
247 my $cust_pkg = $opt{'cust_pkg'};
248 my $part_pkg = $opt{'part_pkg'};
249 my $curuser = $FS::CurrentUser::CurrentUser;
250 my $width = $opt{'cust_pkg-display_times'} ? '38%' : '56%';
251 my $supplemental = $opt{'supplemental'};
253 $opt{colspan} = $opt{'cust_pkg-display_times'} ? 8 : 4;
255 #false laziness w/edit/REAL_cust_pkg.cgi
256 my( $billed_or_prepaid, $last_bill_or_renewed, $next_bill_or_prepaid_until );
257 unless ( $part_pkg->is_prepaid ) {
258 $billed_or_prepaid = 'billed';
259 $last_bill_or_renewed = 'Last bill';
260 $next_bill_or_prepaid_until = 'Next bill';
262 $billed_or_prepaid = 'prepaid';
263 $last_bill_or_renewed = 'Renewed';
264 $next_bill_or_prepaid_until = 'Prepaid until';
270 my $part_pkg = shift;
271 my $freq = $part_pkg->freq_pretty;
272 #$freq =~ s/ / /g;
276 #false laziness w/package.html
278 my($action, $label, $cust_pkg) = @_;
279 return '' unless $cust_pkg;
280 qq!<a href="$p$action.cgi?!. $cust_pkg->pkgnum. qq!">$label</a>!;
284 my( $cust_pkg, $title, $field, %opt ) = @_;
286 if ( $field and $cust_pkg->main_pkgnum ) {
287 # for supplemental packages, we mostly only show these if they're
288 # different from the main package
289 my $main_pkg = $cust_pkg-> main_pkg;
290 if ( $main_pkg->get($field) ne $cust_pkg->get($field)
291 # with some exceptions
293 or $field eq 'last_bill'
296 or $field eq 'cancel'
304 my $color = $opt{'color'};
306 my $html = qq(<TR><TD WIDTH="$width" ALIGN="right">);
307 $html .= qq(<FONT COLOR="#$color"><B>) if length($color);
308 $html .= qq($title );
309 $html .= qq(</B></FONT>) if length($color);
311 if ( $opt{'pkg_balances'} && ! $cust_pkg->{_printed_balance}++ ) { #kludge
312 $html .= ' (Balance: <B>'. $opt{'money_char'}.
313 $cust_pkg->cust_main->balance_pkgnum($cust_pkg->pkgnum).
318 $html .= pkg_datestr($cust_pkg, $field, %opt). '</TR>';
323 sub pkg_status_row_if {
324 my( $cust_pkg, $title, $field, %opt ) = @_;
326 $title = '<FONT SIZE=-1>( '. pkg_unadjourn_link($cust_pkg). ' ) </FONT>'. $title
327 if ( $field eq 'adjourn' &&
328 $opt{curuser}->access_right('Suspend customer package later')
331 $title = '<FONT SIZE=-1>( '. pkg_unexpire_link($cust_pkg). ' ) </FONT>'. $title
332 if ( $field eq 'expire' &&
333 $opt{curuser}->access_right('Cancel customer package later')
336 $cust_pkg->get($field) ? pkg_status_row($cust_pkg, $title, $field, %opt) : '';
339 sub pkg_status_row_changed {
340 my( $cust_pkg, %opt ) = @_;
342 return '' unless $cust_pkg->change_date;
345 pkg_status_row( $cust_pkg, emt('Package changed'), 'change_date', %opt );
347 my $old = $cust_pkg->old_cust_pkg;
349 my $part_pkg = $old->part_pkg;
350 $html .= pkg_status_row_colspan( $cust_pkg,
351 emt("Changed from [_1]: [_2]",
352 $cust_pkg->change_pkgnum,
353 $part_pkg->pkg_comment(nopartpkg => 1) ),
363 sub pkg_status_row_noauto {
364 my( $cust_pkg, %opt ) = @_;
365 my $part_pkg = $opt{'part_pkg'};
366 return '' unless $cust_pkg->no_auto || $part_pkg->no_auto;
368 #inefficient, should be passed in
369 my $cust_main = $cust_pkg->cust_main;
371 return '' unless $cust_main->payby =~ /^(CARD|CHEK)$/;
372 my $what = lc(FS::payby->shortname($cust_main->payby));
374 pkg_status_row_colspan( $cust_pkg, emt("No automatic $what charge"), '');
377 sub pkg_status_row_discount {
378 my( $cust_pkg, %opt ) = @_;
382 foreach my $cust_pkg_discount ( $cust_pkg->cust_pkg_discount_active ) {
384 my $discount = $cust_pkg_discount->discount;
386 my $label = '<B>'.emt('Discount').'</B>: '. $discount->description;
387 if ( $discount->months ) {
388 my $remaining = $discount->months - $cust_pkg_discount->months_used;
389 $remaining = sprintf('%.2f', $remaining) if $remaining =~ /\./;
390 $label .= emt("([_1] months remaining)",$remaining);
393 $label .= ' <FONT SIZE="-1">('.
394 '<A HREF="../misc/delete-cust_pkg_discount.html?'.
395 $cust_pkg_discount->pkgdiscountnum.
396 '">'.emt('remove discount').'</A>)</FONT>';
398 $html .= pkg_status_row_colspan( $cust_pkg, $label, '', %opt );
406 my ($cust_pkg, $cpr, %opt) = @_;
407 return '' if $cust_pkg->main_pkgnum;
410 $reasontext = $cpr->reasontext . ' by ' . $cpr->otaker if $cpr;
411 pkg_status_row_colspan( $cust_pkg, $reasontext, '',
412 'align'=>'right', 'size'=>'-2', %opt
416 sub pkg_status_row_colspan {
417 my($cust_pkg, $title, $addl, %opt) = @_;
419 my $colspan = $opt{'colspan'};
421 my $align = $opt{'align'} ? 'ALIGN="'. $opt{'align'}.'"' : '';
422 my $color = $opt{'color'} ? 'COLOR="#'.$opt{'color'}.'"' : '';
423 my $size = $opt{'size'} ? 'SIZE="'. $opt{'size'}. '"' : '';
425 my $html = qq(<TR><TD COLSPAN=$colspan $align>);
426 $html .= qq(<FONT $color $size>) if length($color) || $size;
427 $html .= qq(<B>) if $color && !$size;
429 $html .= qq(</B>) if $color && !$size;
430 $html .= qq(</FONT>) if length($color) || $size;
431 $html .= ", $addl" if length($addl);
433 if ( $opt{'pkg-balances'} && ! $cust_pkg->{_printed_balance}++ ) { #kludge
434 $html .= ' (Balance: <B>'. $opt{'money_char'}.
435 $cust_pkg->cust_main->balance_pkgnum($cust_pkg->pkgnum).
439 $html .= qq(</TD></TR>);
446 my($cust_pkg, $field, %opt) = @_ or return '';
447 return ' ' unless $cust_pkg->get($field);
448 my $format = '<TD align="left"><B>%b</B></TD>'.
449 '<TD align="right"><B> %o,</B></TD>'.
450 '<TD align="right"><B> %Y</B></TD>';
451 #$format .= ' <FONT SIZE=-3>%l:%M:%S%P %z</FONT>'
452 $format .= '<TD ALIGN="right"><B> %l</TD>'.
453 '<TD ALIGN="center"><B>:</B></TD>'.
454 '<TD ALIGN="left"><B>%M</B></TD>'.
455 '<TD ALIGN="left"><B> %P</B></TD>'
456 if $opt{'cust_pkg-display_times'};
457 my $strip = time2str($format, $cust_pkg->get($field) );
458 $strip =~ s/ (\d)/$1/g;
462 sub pkg_suspend_link {
463 include( '/elements/popup_link-cust_pkg.html',
464 'action' => $p. 'misc/cancel_pkg.html?method=suspend',
465 'label' => emt('Suspend now'),
466 'actionlabel' => emt('Suspend'),
467 'color' => '#FF9900',
472 sub pkg_adjourn_link {
473 include( '/elements/popup_link-cust_pkg.html',
474 'action' => $p. 'misc/cancel_pkg.html?method=adjourn',
475 'label' => emt('Suspend later'),
476 'actionlabel' => emt('Adjourn'),
477 'color' => '#CC6600',
483 my($cust_pkg) = shift;
484 include( '/elements/popup_link-cust_pkg.html',
485 'action' => $p. 'misc/delay_susp_pkg.html',
486 'label' => ( $cust_pkg->dundate
487 ? emt('Edit suspension delay')
488 : emt('Delay suspend')
490 'actionlabel' => emt('Delay suspend for'),
491 'cust_pkg' => $cust_pkg,
495 sub pkg_resume_link {
496 include( '/elements/popup_link-cust_pkg.html',
497 'action' => $p. 'misc/cancel_pkg.html?method=resume',
498 'label' => emt('Unsuspend later'),
499 'actionlabel' => emt('Resume'),
500 'color' => '#00CC00',
505 sub pkg_unsuspend_link { pkg_link('misc/unsusp_pkg', emt('Unsuspend now'), @_ ); }
506 sub pkg_unadjourn_link { pkg_link('misc/unadjourn_pkg', emt('Abort'), @_ ); }
507 sub pkg_unexpire_link { pkg_link('misc/unexpire_pkg', emt('Abort'), @_ ); }
509 sub pkg_cancel_link {
510 include( '/elements/popup_link-cust_pkg.html',
511 'action' => $p. 'misc/cancel_pkg.html?method=cancel',
512 'label' => emt('Cancel now'),
513 'actionlabel' => emt('Cancel'),
514 'color' => '#ff0000',
519 sub pkg_uncancel_link {
520 include( '/elements/popup_link-cust_pkg.html',
521 'action' => $p. 'misc/cancel_pkg.html?method=uncancel',
522 'label' => emt('Un-cancel'),
523 'actionlabel' => emt('Un-cancel'),
529 sub pkg_expire_link {
530 include( '/elements/popup_link-cust_pkg.html',
531 'action' => $p. 'misc/cancel_pkg.html?method=expire',
532 'label' => emt('Cancel later'),
533 'actionlabel' => emt('Expire'),
534 'color' => '#CC0000',
539 sub svc_recharge_link {
540 include( '/elements/popup_link-cust_svc.html',
541 'action' => $p. 'misc/recharge_svc.html',
542 'label' => emt('Recharge'),
543 'actionlabel' => emt('Recharge'),
544 'color' => '#333399',
549 sub pkg_autosuspend_time {
550 my $cust_pkg = shift or return '';
553 my $pending_suspend = 0;
554 #this seems to be extremely inefficient... and is slowing down all customer
558 grep { $_->part_event->action eq 'suspend' }
559 @{$cust_pkg->cust_main->due_cust_event( time => $time + 86400*$days,
565 $pending_suspend = 1;
569 $pending_suspend ? time + ($days + 1) * 86400 : '';