diff options
| author | ivan <ivan> | 2004-12-11 07:51:00 +0000 | 
|---|---|---|
| committer | ivan <ivan> | 2004-12-11 07:51:00 +0000 | 
| commit | c21b6e69583bec632de7fcd340c29982c868f73d (patch) | |
| tree | 163052140804fe30a8a5741834212421e13d63d9 /httemplate | |
| parent | 9ef0fe556c750abdea4b10e720af0d23485a45d8 (diff) | |
voiding of echeck payments instead of refunds
Diffstat (limited to 'httemplate')
| -rwxr-xr-x | httemplate/view/cust_main.cgi | 769 | ||||
| -rw-r--r-- | httemplate/view/cust_main/order_pkg.html | 39 | ||||
| -rwxr-xr-x | httemplate/view/cust_main/packages.html | 350 | ||||
| -rw-r--r-- | httemplate/view/cust_main/payment_history.html | 357 | ||||
| -rw-r--r-- | httemplate/view/cust_main/quick-charge.html | 32 | 
5 files changed, 791 insertions, 756 deletions
| diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi index 322f0674e..d5d77f2a5 100755 --- a/httemplate/view/cust_main.cgi +++ b/httemplate/view/cust_main.cgi @@ -6,16 +6,16 @@ my $conf = new FS::Conf;  my %uiview = ();  my %uiadd = ();  foreach my $part_svc ( qsearch('part_svc',{}) ) { -  $uiview{$part_svc->svcpart} = popurl(2). "view/". $part_svc->svcdb . ".cgi"; -  $uiadd{$part_svc->svcpart}= popurl(2). "edit/". $part_svc->svcdb . ".cgi"; +  $uiview{$part_svc->svcpart} = $p. "view/". $part_svc->svcdb . ".cgi"; +  $uiadd{$part_svc->svcpart}= $p. "edit/". $part_svc->svcdb . ".cgi";  } -print header("Customer View", menubar( -  'Main Menu' => popurl(2) -)); -  %> +<%= header("Customer View", menubar( +  'Main Menu' => $p, +)) %> +  <STYLE TYPE="text/css">  .package TH { font-size: medium }  .package TR { font-size: smaller } @@ -108,762 +108,19 @@ Comments    <%= include('cust_main/tickets.html', $cust_main ) %>  <% } %> -<BR> -<SCRIPT TYPE="text/javascript"> -function enable_order_pkg () { -  if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) { -    document.OrderPkgForm.submit.disabled = false; -  } else { -    document.OrderPkgForm.submit.disabled = true; -  } -} -</SCRIPT> -<FORM NAME="OrderPkgForm" ACTION="<%= $p %>edit/process/quick-cust_pkg.cgi" METHOD="POST"> -<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $custnum %>"> -<SELECT NAME="pkgpart" onChange="enable_order_pkg()"><OPTION>Order additional package - -<% - -my @agents = qsearch( 'agent', {} ); -my $agent; -unless ( scalar(@agents) == 1 ) { -  $agent = qsearchs('agent',{ 'agentnum' => $cust_main->agentnum } ); -} else { -  $agent = $agents[0]; -} - -foreach my $part_pkg ( -  qsearch( 'part_pkg', { 'disabled' => '' }, '', -           ' AND 0 < ( SELECT COUNT(*) FROM type_pkgs '. -           '             WHERE typenum = '. $agent->typenum. -           '             AND type_pkgs.pkgpart = part_pkg.pkgpart )' -         ) -) { -%> -<OPTION VALUE="<%= $part_pkg->pkgpart %>"><%= $part_pkg->pkg %> - <%= $part_pkg->comment %> -<% } %> - -</SELECT><INPUT NAME="submit" TYPE="submit" VALUE="Order Package" disabled></FORM> - -<% - -if ( $conf->config('payby-default') ne 'HIDE' ) { - -  print -    qq!<FORM ACTION="${p}edit/process/quick-charge.cgi" METHOD="POST">!. -    qq!<INPUT TYPE="hidden" NAME="custnum" VALUE="$custnum">!. -    qq!Description:<INPUT TYPE="text" NAME="pkg">!. -    qq! Amount:<INPUT TYPE="text" NAME="amount" SIZE=6>!. -    qq! !; -   -  #false laziness w/ edit/part_pkg.cgi -  if ( $conf->exists('enable_taxclasses') ) { -    print '<SELECT NAME="taxclass">'; -    my $sth = dbh->prepare('SELECT DISTINCT taxclass FROM cust_main_county') -      or die dbh->errstr; -    $sth->execute or die $sth->errstr; -    foreach my $taxclass ( map $_->[0], @{$sth->fetchall_arrayref} ) { -      print qq!<OPTION VALUE="$taxclass"!; -      #print ' SELECTED' if $taxclass eq $hashref->{taxclass}; -      print qq!>$taxclass</OPTION>!; -    } -    print '</SELECT>'; -  } else { -    print '<INPUT TYPE="hidden" NAME="taxclass" VALUE="">'; -  } -   -  print qq!<INPUT TYPE="submit" VALUE="One-time charge"></FORM><BR>!; - -} - -print qq!<A NAME="cust_pkg">Packages</A> !, -      qq!( <A HREF="!, popurl(2), qq!edit/cust_pkg.cgi?$custnum">Bulk order and cancel packages</A> (preserves services) )!, -; - -#begin display packages - -#get package info - -my $packages = get_packages($cust_main, $conf); - -if ( @$packages ) { -%> -<TABLE CLASS="package" BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999"> -<TR> -  <TH>Package</TH> -  <TH>Status</TH> -  <TH COLSPAN=2>Services</TH> -</TR> -<% -foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) { -  my $rowspan = 0; - -  if ($pkg->{cancel}) { -    $rowspan = 0; -  } else { -    foreach my $svcpart (@{$pkg->{svcparts}}) { -      $rowspan += $svcpart->{count}; -      $rowspan++ if ($svcpart->{count} < $svcpart->{quantity}); -    } -  }  - -%> -<!--pkgnum: <%=$pkg->{pkgnum}%>--> -<TR> -  <TD ROWSPAN=<%=$rowspan%>> -    <A NAME="cust_pkg<%=$pkg->{pkgnum}%>"><%=$pkg->{pkgnum}%></A>: -    <%=$pkg->{pkg}%> - <%=$pkg->{comment}%><BR> -<% unless ($pkg->{cancel}) { %> -    ( <%=pkg_change_link($pkg)%> ) -    ( <%=pkg_dates_link($pkg)%> | <%=pkg_customize_link($pkg,$custnum)%> ) -<% } %> -  </TD> -<% -  #foreach (qw(setup last_bill next_bill susp expire cancel)) { -  #  print qq!  <TD ROWSPAN=$rowspan>! . pkg_datestr($pkg,$_,$conf) . qq!</TD>\n!; -  #} -  print "<TD ROWSPAN=$rowspan>". &itable(''); - -  sub myfreq { -    my $part_pkg = shift; -    my $freq = $part_pkg->freq_pretty; -    $freq =~ s/ / /g; -    $freq; -  } - -  if ( $pkg->{cancel} ) { #status: cancelled - -    print '<TR><TD><FONT COLOR="#ff0000"><B>Cancelled </B></FONT></TD>'. -          '<TD>'. pkg_datestr($pkg,'cancel',$conf). '</TD></TR>'; -    unless ( $pkg->{setup} ) { -      print '<TR><TD COLSPAN=2>Never billed</TD></TR>'; -    } else { -      print "<TR><TD>Setup </TD><TD>". -            pkg_datestr($pkg, 'setup',$conf). '</TD></TR>'; -      print "<TR><TD>Last bill </TD><TD>". -            pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>' -        if $pkg->{'last_bill'}; -      print "<TR><TD>Suspended </TD><TD>". -            pkg_datestr($pkg, 'susp',$conf). '</TD></TR>' -        if $pkg->{'susp'}; -    } - -  } else { - -    if ( $pkg->{susp} ) { #status: suspended -      print '<TR><TD><FONT COLOR="#FF9900"><B>Suspended</B> </FONT></TD>'. -            '<TD>'. pkg_datestr($pkg,'susp',$conf). '</TD></TR>'; -      unless ( $pkg->{setup} ) { -        print '<TR><TD COLSPAN=2>Never billed</TD></TR>'; -      } else { -        print "<TR><TD>Setup </TD><TD>".  -              pkg_datestr($pkg, 'setup',$conf). '</TD></TR>'; -      } -      print "<TR><TD>Last bill </TD><TD>". -            pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>' -        if $pkg->{'last_bill'}; -      # next bill ?? -      print "<TR><TD>Expires </TD><TD>". -            pkg_datestr($pkg, 'expire',$conf). '</TD></TR>' -        if $pkg->{'expire'}; -      print '<TR><TD COLSPAN=2>( '. pkg_unsuspend_link($pkg). -            ' | '. pkg_cancel_link($pkg). ' )</TD></TR>'; - -    } else { #status: active - -      unless ( $pkg->{setup} ) { #not setup - -        print '<TR><TD COLSPAN=2>Not yet billed ('; -        unless ( $pkg->{freq} ) { -          print 'one-time charge)</TD></TR>'; -          print '<TR><TD COLSPAN=2>( '. pkg_cancel_link($pkg). -                ' )</TD</TR>'; -        } else { -          print 'billed '. myfreq($pkg->{part_pkg}). ')</TD></TR>'; -        } - -      } else { #setup - -        unless ( $pkg->{freq} ) { -          print "<TR><TD COLSPAN=2>One-time charge</TD></TR>". -                '<TR><TD>Billed </TD><TD>'. -                pkg_datestr($pkg,'setup',$conf). '</TD></TR>'; -        } else { -          print '<TR><TD COLSPAN=2><FONT COLOR="#00CC00"><B>Active</B></FONT>'. -                ', billed '. myfreq($pkg->{part_pkg}). '</TD></TR>'. -                '<TR><TD>Setup </TD><TD>'. -                pkg_datestr($pkg, 'setup',$conf). '</TD></TR>'; -        } - -      } - -      print "<TR><TD>Last bill </TD><TD>". -            pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>' -        if $pkg->{'last_bill'}; -      print "<TR><TD>Next bill </TD><TD>". -            pkg_datestr($pkg, 'next_bill',$conf). '</TD></TR>' -        if $pkg->{'next_bill'}; -      print "<TR><TD>Expires </TD><TD>". -            pkg_datestr($pkg, 'expire',$conf). '</TD></TR>' -        if $pkg->{'expire'}; -      if ( $pkg->{freq} ) { -        print '<TR><TD COLSPAN=2>( '. pkg_suspend_link($pkg). -              ' | '. pkg_cancel_link($pkg). ' )</TD></TR>'; -      } - -    } - -  } - -  print "</TABLE></TD>\n"; - -  if ($rowspan == 0) { print qq!</TR>\n!; next; } - -  my $cnt = 0; -  foreach my $svcpart (sort {$a->{svcpart} <=> $b->{svcpart}} @{$pkg->{svcparts}}) { -    foreach my $service (@{$svcpart->{services}}) { -      print '<TR>' if ($cnt > 0); -%> -  <TD><%=svc_link($svcpart,$service)%></TD> -  <TD><%=svc_label_link($svcpart,$service)%><BR>( <%=svc_unprovision_link($service)%> )</TD> -</TR> -<% -      $cnt++; -    } -    if ($svcpart->{count} < $svcpart->{quantity}) { -      print qq!<TR>\n! if ($cnt > 0); -      print qq!  <TD COLSPAN=2>!.svc_provision_link($pkg, $svcpart, $conf).qq!</TD>\n</TR>\n!; -    } -  } -} -print '</TABLE>'; -} - -#end display packages -%> +<BR><BR> +<%= include('cust_main/order_pkg.html', $cust_main ) %>  <% if ( $conf->config('payby-default') ne 'HIDE' ) { %> -   -  <BR><BR><A NAME="history"><FONT SIZE="+2">Payment History</FONT></A><BR> -  <A HREF="<%= $p %>edit/cust_pay.cgi?custnum=<%= $custnum %>">Post cash/check payment</A> -  | <A HREF="<%= $p %>misc/payment.cgi?payby=CARD;custnum=<%= $custnum %>">Process credit card payment</A> -  | <A HREF="<%= $p %>misc/payment.cgi?payby=CHEK;custnum=<%= $custnum %>">Process electronic check (ACH) payment</A> -  <BR><A HREF="<%= $p %>edit/cust_credit.cgi?<%= $custnum %>">Post credit</A> +  <%= include('cust_main/quick-charge.html', $cust_main ) %>    <BR> +<% } %> -  <% -  #get payment history -  my @history = (); - -  #invoices -  foreach my $cust_bill ($cust_main->cust_bill) { -    my $pre = ( $cust_bill->owed > 0 ) -                ? '<B><FONT SIZE="+1" COLOR="#FF0000">Open ' -                : ''; -    my $post = ( $cust_bill->owed > 0 ) ? '</FONT></B>' : ''; -    my $invnum = $cust_bill->invnum; -    push @history, { -      'date'   => $cust_bill->_date, -      'desc'   => qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!. $pre. -                  "Invoice #$invnum (Balance \$". $cust_bill->owed. ')'. -                  $post. '</A>', -      'charge' => $cust_bill->charged, -    }; -  } - -  #payments (some false laziness w/credits) -  foreach my $cust_pay ($cust_main->cust_pay) { - -    my $payby = $cust_pay->payby; -    my $payinfo = $payby eq 'CARD' -                    ? $cust_pay->payinfo_masked -                    : $cust_pay->payinfo; -    my @cust_bill_pay = $cust_pay->cust_bill_pay; -    my @cust_pay_refund = $cust_pay->cust_pay_refund; - -    my $target = "$payby$payinfo"; -    $payby =~ s/^BILL$/Check #/ if $payinfo; -    $payby =~ s/^CHEK$/Electronic check /; -    $payby =~ s/^BILL$//; -    $payby =~ s/^(CARD|COMP)$/$1 /; -    my $info = $payby ? " ($payby$payinfo)" : ''; - -    my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' ); -    if (    scalar(@cust_bill_pay)   == 0 -         && scalar(@cust_pay_refund) == 0 ) { -      #completely unapplied -      $pre = '<B><FONT COLOR="#FF0000">Unapplied '; -      $post = '</FONT></B>'; -      $apply = qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!. -               $cust_pay->paynum. '">apply</A>)'; -    } elsif (    scalar(@cust_bill_pay)   == 1 -              && scalar(@cust_pay_refund) == 0 -              && $cust_pay->unapplied == 0     ) { -      #applied to one invoice, the usual situation -      $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum; -    } elsif (    scalar(@cust_bill_pay)   == 0 -              && scalar(@cust_pay_refund) == 1 -              && $cust_pay->unapplied == 0     ) { -      #applied to one refund -      $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date); -    } else { -      #complicated -      $desc = '<BR>'; -      foreach my $app ( sort { $a->_date <=> $b->_date } -                             ( @cust_bill_pay, @cust_pay_refund ) ) { -        if ( $app->isa('FS::cust_bill_pay') ) { -          $desc .= '  '. -                   '$'. $app->amount. -                   ' applied to Invoice #'. $app->invnum. -                   '<BR>'; -                   #' on '. time2str("%D", $cust_bill_pay->_date). -        } elsif ( $app->isa('FS::cust_pay_refund') ) { -          $desc .= '  '. -                   '$'. $app->amount. -                   ' refunded on'. time2str("%D", $app->_date). -                   '<BR>'; -        } else { -          die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund"; -        } -      } -      if ( $cust_pay->unapplied > 0 ) { -        $desc .= '  '. -                 '<B><FONT COLOR="#FF0000">$'. -                 $cust_pay->unapplied. ' unapplied</FONT></B>'. -                 qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!. -                 $cust_pay->paynum. '">apply</A>)'. -                 '<BR>'; -      } -    } - -    my $refund = ''; -    my $refund_days = $conf->config('card_refund-days') || 120; -    if (    $cust_pay->closed !~ /^Y/i -         && $cust_pay->payby =~ /^(CARD|CHEK)$/ -         && time-$cust_pay->_date < $refund_days*86400 -         && $cust_pay->unrefunded > 0 -    ) { -      $refund = qq! (<A HREF="!. qq!${p}edit/cust_refund.cgi?payby=$1;!. -                qq!paynum=!. $cust_pay->paynum. qq!">refund</A>)!; -    } - -    my $void = ''; -    if (    $cust_pay->closed !~ /^Y/i -         && $cust_pay->payby !~  /^(CARD|CHEK)$/ -       ) { -      $void = qq! (<A HREF="javascript:areyousure('!. -              qq!${p}misc/void-cust_pay.cgi?!. $cust_pay->paynum. -              qq!', 'Are you sure you want to void this payment?')">!. -              qq!void</A>)!; -    } - -    my $delete = ''; -    if ( $cust_pay->closed !~ /^Y/i && $conf->exists('deletepayments') ) { -      $delete = qq! (<A HREF="javascript:areyousure('!. -                qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum. -                qq!', 'Are you sure you want to delete this payment?')">!. -                qq!delete</A>)!; -    } - -    my $unapply = ''; -    if (    $cust_pay->closed !~ /^Y/i -         && $conf->exists('unapplypayments') -         && scalar(@cust_bill_pay)           ) { -      $unapply = qq! (<A HREF="javascript:areyousure('!. -                 qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum. -                 qq!', 'Are you sure you want to unapply this payment?')">!. -                 qq!unapply</A>)!; -    } - -    push @history, { -      'date'    => $cust_pay->_date, -      'desc'    => $pre. "Payment$post$info$desc". -                   "$apply$refund$void$delete$unapply", -      'payment' => $cust_pay->paid, -      'target'  => $target, -    }; -  } - -  #voided payments -  foreach my $cust_pay_void ($cust_main->cust_pay_void) { - -    my $payby = $cust_pay_void->payby; -    my $payinfo = $payby eq 'CARD' -                    ? $cust_pay_void->payinfo_masked -                    : $cust_pay_void->payinfo; - -    $payby =~ s/^BILL$/Check #/ if $payinfo; -    $payby =~ s/^CHEK$/Electronic check /; -    $payby =~ s/^BILL$//; -    $payby =~ s/^(CARD|COMP)$/$1 /; -    my $info = $payby ? " ($payby$payinfo)" : ''; - -    push @history, { -      'date'   => $cust_pay_void->_date, -      'desc'   => "<DEL>Payment $info</DEL> <I>voided ". -                  time2str("%D", $cust_pay_void->void_date). -                  " by ". $cust_pay_void->otaker. '</i>', -      'void_payment' => $cust_pay_void->paid, -    }; -   -  } - -  #credits (some false laziness w/payments) -  foreach my $cust_credit ($cust_main->cust_credit) { - -    my @cust_credit_bill = $cust_credit->cust_credit_bill; -    my @cust_credit_refund = $cust_credit->cust_credit_refund; - -    my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' ); -    if (    scalar(@cust_credit_bill)   == 0 -         && scalar(@cust_credit_refund) == 0 ) { -      #completely unapplied -      $pre = '<B><FONT COLOR="#FF0000">Unapplied '; -      $post = '</FONT></B>'; -      $apply = qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!. -               $cust_credit->crednum. '">apply</A>)'; -    } elsif (    scalar(@cust_credit_bill)   == 1 -              && scalar(@cust_credit_refund) == 0 -              && $cust_credit->credited == 0      ) { -      #applied to one invoice, the usual situation -      $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum; -    } elsif (    scalar(@cust_credit_bill)   == 0 -              && scalar(@cust_credit_refund) == 1 -              && $cust_credit->credited == 0      ) { -      #applied to one refund -      $desc = ' refunded on '.  time2str("%D", $cust_credit_refund[0]->_date); -    } else { -      #complicated -      $desc = '<BR>'; -      foreach my $app ( sort { $a->_date <=> $b->_date } -                             ( @cust_credit_bill, @cust_credit_refund ) ) { -        if ( $app->isa('FS::cust_credit_bill') ) { -          $desc .= '  '. -                   '$'. $app->amount. -                   ' applied to Invoice #'. $app->invnum. -                   '<BR>'; -                   #' on '. time2str("%D", $app->_date). -        } elsif ( $app->isa('FS::cust_credit_refund') ) { -          $desc .= '  '. -                   '$'. $app->amount. -                   ' refunded on'. time2str("%D", $app->_date). -                   '<BR>'; -        } else { -          die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund"; -        } -      } -      if ( $cust_credit->credited > 0 ) { -        $desc .= '  <B><FONT COLOR="#FF0000">$'. -                 $cust_credit->credited. ' unapplied</FONT></B>'. -                 qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!. -                 $cust_credit->crednum. '">apply</A>)'. -                 '<BR>'; -      } -    } -# -    my $delete = ''; -    if ( $cust_credit->closed !~ /^Y/i && $conf->exists('deletecredits') ) { -      $delete = qq! (<A HREF="javascript:areyousure('!. -                qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum. -                qq!', 'Are you sure you want to delete this credit?')">!. -                qq!delete</A>)!; -    } -     -    my $unapply = ''; -    if (    $cust_credit->closed !~ /^Y/i -         && $conf->exists('unapplycredits') -         && scalar(@cust_credit_bill)       ) { -      $unapply = qq! (<A HREF="javascript:areyousure('!. -                 qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum. -                 qq!', 'Are you sure you want to unapply this credit?')">!. -                 qq!unapply</A>)!; -    } -     -    push @history, { -      'date'   => $cust_credit->_date, -      'desc'   => $pre. "Credit$post by ". $cust_credit->otaker. -                  ' ('. $cust_credit->reason. ')'. -                  "$desc$apply$delete$unapply", -      'credit' => $cust_credit->amount, -    }; - -  } - -  #refunds -  foreach my $cust_refund ($cust_main->cust_refund) { - -    my $payby = $cust_refund->payby; -    my $payinfo = $payby eq 'CARD' -                    ? $cust_refund->payinfo_masked -                    : $cust_refund->payinfo; - -    $payby =~ s/^BILL$/Check #/ if $payinfo; -    $payby =~ s/^CHEK$/Electronic check /; -    $payby =~ s/^(CARD|COMP)$/$1 /; - -    push @history, { -      'date'   => $cust_refund->_date, -      'desc'   => "Refund ($payby$payinfo) by ". $cust_refund->otaker, -      'refund' => $cust_refund->refund, -    }; -   -  } -   -  %> -   -  <%= table() %> -  <TR> -    <TH>Date</TH> -    <TH>Description</TH> -    <TH><FONT SIZE=-1>Charge</FONT></TH> -    <TH><FONT SIZE=-1>Payment</FONT></TH> -    <TH><FONT SIZE=-1>In-house<BR>Credit</FONT></TH> -    <TH><FONT SIZE=-1>Refund</FONT></TH> -    <TH><FONT SIZE=-1>Balance</FONT></TH> -  </TR> - -  <% -  #display payment history - -  my %target; -  my $balance = 0; -  foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) { - -    my $charge  = exists($item->{'charge'}) -                    ? sprintf('$%.2f', $item->{'charge'}) -                    : ''; -    my $payment = exists($item->{'payment'}) -                    ? sprintf('- $%.2f', $item->{'payment'}) -                    : ''; -    $payment ||= sprintf('<DEL>- $%.2f</DEL>', $item->{'void_payment'}) -      if exists($item->{'void_payment'}); -    my $credit  = exists($item->{'credit'}) -                    ? sprintf('- $%.2f', $item->{'credit'}) -                    : ''; -    my $refund  = exists($item->{'refund'}) -                    ? sprintf('$%.2f', $item->{'refund'}) -                    : ''; - -    my $target = exists($item->{'target'}) ? $item->{'target'} : ''; - -    $balance += $item->{'charge'}  if exists $item->{'charge'}; -    $balance -= $item->{'payment'} if exists $item->{'payment'}; -    $balance -= $item->{'credit'}  if exists $item->{'credit'}; -    $balance += $item->{'refund'}  if exists $item->{'refund'}; -    $balance = sprintf("%.2f", $balance); -    $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp -    ( my $showbalance = '$'. $balance ) =~ s/^\$\-/- \$/; - -  %> -   -    <TR> -      <TD> -        <% unless ( !$target || $target{$target}++ ) { %> -          <A NAME="<%= $target %>"> -        <% } %> -        <%= time2str("%D",$item->{'date'}) %> -        <% if ( $target && $target{$target} == 1 ) { %> -          </A> -        <% } %> -        </FONT> -      </TD> -      <TD><%= $item->{'desc'} %></TD> -      <TD ALIGN="right"><%= $charge  %></TD> -      <TD ALIGN="right"><%= $payment %></TD> -      <TD ALIGN="right"><%= $credit  %></TD> -      <TD ALIGN="right"><%= $refund  %></TD> -      <TD ALIGN="right"><%= $showbalance %></TD> -    </TR> - -  <% } %> -   -  </TABLE> +<%= include('cust_main/packages.html', $cust_main ) %> +<% if ( $conf->config('payby-default') ne 'HIDE' ) { %> +  <%= include('cust_main/payment_history.html', $cust_main ) %>  <% } %>  </BODY></HTML> -<% -#subroutines - -sub get_packages { -  my $cust_main = shift or return undef; -  my $conf = shift; -   -  my @packages = (); -   -  foreach my $cust_pkg ( -    $conf->exists('hidecancelledpackages') -      ? $cust_main->ncancelled_pkgs -      : $cust_main->all_pkgs -  ) {  -   -    my $part_pkg = $cust_pkg->part_pkg; - -    my %pkg = (); - -    #to get back to the original object... should use it in the first place!! -    $pkg{cust_pkg} = $cust_pkg; -    $pkg{part_pkg} = $part_pkg; - -    $pkg{pkgnum} = $cust_pkg->pkgnum; -    $pkg{pkg} = $part_pkg->pkg; -    $pkg{pkgpart} = $part_pkg->pkgpart; -    $pkg{comment} = $part_pkg->getfield('comment'); -    $pkg{freq} = $part_pkg->freq; -    $pkg{setup} = $cust_pkg->getfield('setup'); -    $pkg{last_bill} = $cust_pkg->getfield('last_bill'); -    $pkg{next_bill} = $cust_pkg->getfield('bill'); -    $pkg{susp} = $cust_pkg->getfield('susp'); -    $pkg{expire} = $cust_pkg->getfield('expire'); -    $pkg{cancel} = $cust_pkg->getfield('cancel'); - -   -    my %svcparts = map { -      $_->svcpart => { -                       $_->part_svc->hash, -                       'quantity' => $_->quantity, -                       'count'    => $cust_pkg->num_cust_svc($_->svcpart), -                       #'services' => [], -                     }; -    } $part_pkg->pkg_svc; - -    foreach my $cust_svc ( $cust_pkg->cust_svc ) { -      #warn "svcnum ". $cust_svc->svcnum. " / svcpart ". $cust_svc->svcpart. "\n"; -      my $svc = { -        'svcnum' => $cust_svc->svcnum, -        'label'  => ($cust_svc->label)[1], -      }; - -      #false laziness with above, to catch extraneous services.  whole -      #damn thing should be OO... -      my $svcpart = ( $svcparts{$cust_svc->svcpart} ||= { -        $cust_svc->part_svc->hash, -        'quantity' => 0, -        'count'    => $cust_pkg->num_cust_svc($cust_svc->svcpart), -        #'services' => [], -      } ); - -      push @{$svcpart->{services}}, $svc; - -    } - -    $pkg{svcparts} = [ values %svcparts ]; - -    push @packages, \%pkg; -   -  } -   -  return \@packages; - -} - -sub svc_link { - -  my ($svcpart, $svc) = (shift,shift) or return ''; -  return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svcpart->{svc}</A>!; - -} - -sub svc_label_link { - -  my ($svcpart, $svc) = (shift,shift) or return ''; -  return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svc->{label}</A>!; - -} - -sub svc_provision_link { -  my ($pkg, $svcpart, $conf) = @_; -  ( my $svc_nbsp = $svcpart->{svc} ) =~ s/\s+/ /g; -  my $num_left = $svcpart->{quantity} - $svcpart->{count}; -  my $pkgnum_svcpart = "pkgnum$pkg->{pkgnum}-svcpart$svcpart->{svcpart}"; - -  my $url; -  if ( $svcpart->{svcdb} eq 'svc_external' -       && $conf->exists('svc_external-skip_manual') -  ) { -    $url = "${p}edit/process/$svcpart->{svcdb}.cgi?". -           "pkgnum=$pkg->{pkgnum}&". -           "svcpart=$svcpart->{svcpart}"; -  } else { -    $url = "${p}edit/$svcpart->{svcdb}.cgi?$pkgnum_svcpart"; -  } - -  my $link = qq!<A CLASS="provision" HREF="$url">!. -             "Provision $svc_nbsp ($num_left)</A>"; -  if ( $conf->exists('legacy_link') ) { -    $link .= '<BR>'. -             qq!<A CLASS="provision" HREF="${p}misc/link.cgi?!. -             qq!$pkgnum_svcpart">!. -            "Link to legacy $svc_nbsp ($num_left)</A>"; -  } -  $link; -} - -sub svc_unprovision_link { -  my $svc = shift or return ''; -  qq!<A HREF="javascript:areyousure('${p}misc/unprovision.cgi?$svc->{svcnum}',!. -  qq!'Permanently unprovision and delete this service?')">Unprovision</A>!; -} - -# This should be generalized to use config options to determine order. -sub pkgsort_pkgnum_cancel { -  if ($a->{cancel} and $b->{cancel}) { -    return ($a->{pkgnum} <=> $b->{pkgnum}); -  } elsif ($a->{cancel} or $b->{cancel}) { -    return (-1) if ($b->{cancel}); -    return (1) if ($a->{cancel}); -    return (0); -  } else { -    return($a->{pkgnum} <=> $b->{pkgnum}); -  } -} - -sub pkg_datestr { -  my($pkg, $field, $conf) = @_ or return ''; -  return ' ' unless $pkg->{$field}; -  my $format = $conf->exists('pkg_showtimes') -               ? '<B>%D</B> <FONT SIZE=-3>%l:%M:%S%P %z</FONT>' -               : '<B>%b %o, %Y</B>'; -  ( my $strip = time2str($format, $pkg->{$field}) ) =~ s/ (\d)/$1/g; -  $strip; -} - -sub pkg_change_link { -  my $pkg = shift or return ''; -  return qq!<a href="${p}misc/change_pkg.cgi?$pkg->{pkgnum}">!. -         qq!Change package</a>!; -} - -sub pkg_suspend_link { -  my $pkg = shift or return ''; -  return qq!<a href="${p}misc/susp_pkg.cgi?$pkg->{pkgnum}">Suspend</a>!; -} - -sub pkg_unsuspend_link { -  my $pkg = shift or return ''; -  return qq!<a href="${p}misc/unsusp_pkg.cgi?$pkg->{pkgnum}">Unsuspend</a>!; -} - -sub pkg_cancel_link { -  my $pkg = shift or return ''; -  qq!<A HREF="javascript:areyousure('${p}misc/cancel_pkg.cgi?$pkg->{pkgnum}', !. -  qq!'Permanently delete included services and cancel this package?')">!. -  qq!Cancel now</A> | !. -  qq!<A HREF="${p}misc/expire_pkg.cgi?$pkg->{pkgnum}">Cancel later</A>!; -} - -sub pkg_dates_link { -  my $pkg = shift or return ''; -  qq!<A HREF="${p}edit/REAL_cust_pkg.cgi?$pkg->{pkgnum}">Edit dates</A>!; -} - -sub pkg_customize_link { -  my $pkg = shift or return ''; -  my $custnum = shift; -  qq!<A HREF="${p}edit/part_pkg.cgi?keywords=$custnum;clone=$pkg->{pkgpart};!. -  qq!pkgnum=$pkg->{pkgnum}">Customize</A>!; -} - -%> - diff --git a/httemplate/view/cust_main/order_pkg.html b/httemplate/view/cust_main/order_pkg.html new file mode 100644 index 000000000..ac2d05df2 --- /dev/null +++ b/httemplate/view/cust_main/order_pkg.html @@ -0,0 +1,39 @@ +<% +  my( $cust_main ) = @_; +%> + +<SCRIPT TYPE="text/javascript"> +function enable_order_pkg () { +  if ( document.OrderPkgForm.pkgpart.selectedIndex > 0 ) { +    document.OrderPkgForm.submit.disabled = false; +  } else { +    document.OrderPkgForm.submit.disabled = true; +  } +} +</SCRIPT> + +<FORM NAME="OrderPkgForm" ACTION="<%= $p %>edit/process/quick-cust_pkg.cgi" METHOD="POST"> + +<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $cust_main->custnum %>"> + +<SELECT NAME="pkgpart" onChange="enable_order_pkg()"><OPTION>Order additional package + +<% +foreach my $part_pkg ( +  qsearch( 'part_pkg', { 'disabled' => '' }, '', +           ' AND 0 < ( SELECT COUNT(*) FROM type_pkgs '. +           '             WHERE typenum = '. $cust_main->agent->typenum. +           '             AND type_pkgs.pkgpart = part_pkg.pkgpart )' +         ) +) { +%> + +  <OPTION VALUE="<%= $part_pkg->pkgpart %>"><%= $part_pkg->pkg %> - <%= $part_pkg->comment %> + +<% } %> + +</SELECT> + +<INPUT NAME="submit" TYPE="submit" VALUE="Order Package" disabled> + +</FORM> diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html new file mode 100755 index 000000000..c5a0706d6 --- /dev/null +++ b/httemplate/view/cust_main/packages.html @@ -0,0 +1,350 @@ +<% +  my( $cust_main ) = @_; +  my $conf = new FS::Conf; + +  my $packages = get_packages($cust_main, $conf); +%> + +<A NAME="cust_pkg">Packages</A> +( <A HREF="<%= $p %>edit/cust_pkg.cgi?<%= $cust_main->custnum %>">Bulk order and cancel packages</A> (preserves services) ) + +<% if ( @$packages ) { %> + +<TABLE CLASS="package" BORDER=1 CELLSPACING=0 CELLPADDING=2 BORDERCOLOR="#999999"> +<TR> +  <TH>Package</TH> +  <TH>Status</TH> +  <TH COLSPAN=2>Services</TH> +</TR> + +<% +foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) { +  my $rowspan = 0; + +  if ($pkg->{cancel}) { +    $rowspan = 0; +  } else { +    foreach my $svcpart (@{$pkg->{svcparts}}) { +      $rowspan += $svcpart->{count}; +      $rowspan++ if ($svcpart->{count} < $svcpart->{quantity}); +    } +  }  +%> + +<!--pkgnum: <%=$pkg->{pkgnum}%>--> +<TR> +  <TD ROWSPAN=<%=$rowspan%>> +    <A NAME="cust_pkg<%=$pkg->{pkgnum}%>"><%=$pkg->{pkgnum}%></A>: +    <%=$pkg->{pkg}%> - <%=$pkg->{comment}%><BR> +<% unless ($pkg->{cancel}) { %> +    ( <%=pkg_change_link($pkg)%> ) +    ( <%=pkg_dates_link($pkg)%> | <%=pkg_customize_link($pkg,$cust_main->custnum)%> ) +<% } %> +  </TD> +<% +  #foreach (qw(setup last_bill next_bill susp expire cancel)) { +  #  print qq!  <TD ROWSPAN=$rowspan>! . pkg_datestr($pkg,$_,$conf) . qq!</TD>\n!; +  #} +  print "<TD ROWSPAN=$rowspan>". &itable(''); + +  sub myfreq { +    my $part_pkg = shift; +    my $freq = $part_pkg->freq_pretty; +    $freq =~ s/ / /g; +    $freq; +  } + +  if ( $pkg->{cancel} ) { #status: cancelled + +    print '<TR><TD><FONT COLOR="#ff0000"><B>Cancelled </B></FONT></TD>'. +          '<TD>'. pkg_datestr($pkg,'cancel',$conf). '</TD></TR>'; +    unless ( $pkg->{setup} ) { +      print '<TR><TD COLSPAN=2>Never billed</TD></TR>'; +    } else { +      print "<TR><TD>Setup </TD><TD>". +            pkg_datestr($pkg, 'setup',$conf). '</TD></TR>'; +      print "<TR><TD>Last bill </TD><TD>". +            pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>' +        if $pkg->{'last_bill'}; +      print "<TR><TD>Suspended </TD><TD>". +            pkg_datestr($pkg, 'susp',$conf). '</TD></TR>' +        if $pkg->{'susp'}; +    } + +  } else { + +    if ( $pkg->{susp} ) { #status: suspended +      print '<TR><TD><FONT COLOR="#FF9900"><B>Suspended</B> </FONT></TD>'. +            '<TD>'. pkg_datestr($pkg,'susp',$conf). '</TD></TR>'; +      unless ( $pkg->{setup} ) { +        print '<TR><TD COLSPAN=2>Never billed</TD></TR>'; +      } else { +        print "<TR><TD>Setup </TD><TD>".  +              pkg_datestr($pkg, 'setup',$conf). '</TD></TR>'; +      } +      print "<TR><TD>Last bill </TD><TD>". +            pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>' +        if $pkg->{'last_bill'}; +      # next bill ?? +      print "<TR><TD>Expires </TD><TD>". +            pkg_datestr($pkg, 'expire',$conf). '</TD></TR>' +        if $pkg->{'expire'}; +      print '<TR><TD COLSPAN=2>( '. pkg_unsuspend_link($pkg). +            ' | '. pkg_cancel_link($pkg). ' )</TD></TR>'; + +    } else { #status: active + +      unless ( $pkg->{setup} ) { #not setup + +        print '<TR><TD COLSPAN=2>Not yet billed ('; +        unless ( $pkg->{freq} ) { +          print 'one-time charge)</TD></TR>'; +          print '<TR><TD COLSPAN=2>( '. pkg_cancel_link($pkg). +                ' )</TD</TR>'; +        } else { +          print 'billed '. myfreq($pkg->{part_pkg}). ')</TD></TR>'; +        } + +      } else { #setup + +        unless ( $pkg->{freq} ) { +          print "<TR><TD COLSPAN=2>One-time charge</TD></TR>". +                '<TR><TD>Billed </TD><TD>'. +                pkg_datestr($pkg,'setup',$conf). '</TD></TR>'; +        } else { +          print '<TR><TD COLSPAN=2><FONT COLOR="#00CC00"><B>Active</B></FONT>'. +                ', billed '. myfreq($pkg->{part_pkg}). '</TD></TR>'. +                '<TR><TD>Setup </TD><TD>'. +                pkg_datestr($pkg, 'setup',$conf). '</TD></TR>'; +        } + +      } + +      print "<TR><TD>Last bill </TD><TD>". +            pkg_datestr($pkg, 'last_bill',$conf). '</TD></TR>' +        if $pkg->{'last_bill'}; +      print "<TR><TD>Next bill </TD><TD>". +            pkg_datestr($pkg, 'next_bill',$conf). '</TD></TR>' +        if $pkg->{'next_bill'}; +      print "<TR><TD>Expires </TD><TD>". +            pkg_datestr($pkg, 'expire',$conf). '</TD></TR>' +        if $pkg->{'expire'}; +      if ( $pkg->{freq} ) { +        print '<TR><TD COLSPAN=2>( '. pkg_suspend_link($pkg). +              ' | '. pkg_cancel_link($pkg). ' )</TD></TR>'; +      } + +    } + +  } + +  print "</TABLE></TD>\n"; + +  if ($rowspan == 0) { print qq!</TR>\n!; next; } + +  my $cnt = 0; +  foreach my $svcpart (sort {$a->{svcpart} <=> $b->{svcpart}} @{$pkg->{svcparts}}) { +    foreach my $service (@{$svcpart->{services}}) { +      print '<TR>' if ($cnt > 0); +%> +  <TD><%=svc_link($svcpart,$service)%></TD> +  <TD><%=svc_label_link($svcpart,$service)%><BR>( <%=svc_unprovision_link($service)%> )</TD> +</TR> +<% +      $cnt++; +    } +    if ($svcpart->{count} < $svcpart->{quantity}) { +      print qq!<TR>\n! if ($cnt > 0); +      print qq!  <TD COLSPAN=2>!.svc_provision_link($pkg, $svcpart, $conf).qq!</TD>\n</TR>\n!; +    } +  } +} +print '</TABLE>'; +} + +#end display packages +%> + +<% +#subroutines + +sub get_packages { +  my $cust_main = shift or return undef; +  my $conf = shift; +   +  my @packages = (); +   +  foreach my $cust_pkg ( +    $conf->exists('hidecancelledpackages') +      ? $cust_main->ncancelled_pkgs +      : $cust_main->all_pkgs +  ) {  +   +    my $part_pkg = $cust_pkg->part_pkg; + +    my %pkg = (); + +    #to get back to the original object... should use it in the first place!! +    $pkg{cust_pkg} = $cust_pkg; +    $pkg{part_pkg} = $part_pkg; + +    $pkg{pkgnum} = $cust_pkg->pkgnum; +    $pkg{pkg} = $part_pkg->pkg; +    $pkg{pkgpart} = $part_pkg->pkgpart; +    $pkg{comment} = $part_pkg->getfield('comment'); +    $pkg{freq} = $part_pkg->freq; +    $pkg{setup} = $cust_pkg->getfield('setup'); +    $pkg{last_bill} = $cust_pkg->getfield('last_bill'); +    $pkg{next_bill} = $cust_pkg->getfield('bill'); +    $pkg{susp} = $cust_pkg->getfield('susp'); +    $pkg{expire} = $cust_pkg->getfield('expire'); +    $pkg{cancel} = $cust_pkg->getfield('cancel'); + +   +    my %svcparts = map { +      $_->svcpart => { +                       $_->part_svc->hash, +                       'quantity' => $_->quantity, +                       'count'    => $cust_pkg->num_cust_svc($_->svcpart), +                       #'services' => [], +                     }; +    } $part_pkg->pkg_svc; + +    foreach my $cust_svc ( $cust_pkg->cust_svc ) { +      #warn "svcnum ". $cust_svc->svcnum. " / svcpart ". $cust_svc->svcpart. "\n"; +      my $svc = { +        'svcnum' => $cust_svc->svcnum, +        'label'  => ($cust_svc->label)[1], +      }; + +      #false laziness with above, to catch extraneous services.  whole +      #damn thing should be OO... +      my $svcpart = ( $svcparts{$cust_svc->svcpart} ||= { +        $cust_svc->part_svc->hash, +        'quantity' => 0, +        'count'    => $cust_pkg->num_cust_svc($cust_svc->svcpart), +        #'services' => [], +      } ); + +      push @{$svcpart->{services}}, $svc; + +    } + +    $pkg{svcparts} = [ values %svcparts ]; + +    push @packages, \%pkg; +   +  } +   +  return \@packages; + +} + +sub svc_link { + +  my ($svcpart, $svc) = (shift,shift) or return ''; +  return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svcpart->{svc}</A>!; + +} + +sub svc_label_link { + +  my ($svcpart, $svc) = (shift,shift) or return ''; +  return qq!<A HREF="${p}view/$svcpart->{svcdb}.cgi?$svc->{svcnum}">$svc->{label}</A>!; + +} + +sub svc_provision_link { +  my ($pkg, $svcpart, $conf) = @_; +  ( my $svc_nbsp = $svcpart->{svc} ) =~ s/\s+/ /g; +  my $num_left = $svcpart->{quantity} - $svcpart->{count}; +  my $pkgnum_svcpart = "pkgnum$pkg->{pkgnum}-svcpart$svcpart->{svcpart}"; + +  my $url; +  if ( $svcpart->{svcdb} eq 'svc_external' +       && $conf->exists('svc_external-skip_manual') +  ) { +    $url = "${p}edit/process/$svcpart->{svcdb}.cgi?". +           "pkgnum=$pkg->{pkgnum}&". +           "svcpart=$svcpart->{svcpart}"; +  } else { +    $url = "${p}edit/$svcpart->{svcdb}.cgi?$pkgnum_svcpart"; +  } + +  my $link = qq!<A CLASS="provision" HREF="$url">!. +             "Provision $svc_nbsp ($num_left)</A>"; +  if ( $conf->exists('legacy_link') ) { +    $link .= '<BR>'. +             qq!<A CLASS="provision" HREF="${p}misc/link.cgi?!. +             qq!$pkgnum_svcpart">!. +            "Link to legacy $svc_nbsp ($num_left)</A>"; +  } +  $link; +} + +sub svc_unprovision_link { +  my $svc = shift or return ''; +  qq!<A HREF="javascript:areyousure('${p}misc/unprovision.cgi?$svc->{svcnum}',!. +  qq!'Permanently unprovision and delete this service?')">Unprovision</A>!; +} + +# This should be generalized to use config options to determine order. +sub pkgsort_pkgnum_cancel { +  if ($a->{cancel} and $b->{cancel}) { +    return ($a->{pkgnum} <=> $b->{pkgnum}); +  } elsif ($a->{cancel} or $b->{cancel}) { +    return (-1) if ($b->{cancel}); +    return (1) if ($a->{cancel}); +    return (0); +  } else { +    return($a->{pkgnum} <=> $b->{pkgnum}); +  } +} + +sub pkg_datestr { +  my($pkg, $field, $conf) = @_ or return ''; +  return ' ' unless $pkg->{$field}; +  my $format = $conf->exists('pkg_showtimes') +               ? '<B>%D</B> <FONT SIZE=-3>%l:%M:%S%P %z</FONT>' +               : '<B>%b %o, %Y</B>'; +  ( my $strip = time2str($format, $pkg->{$field}) ) =~ s/ (\d)/$1/g; +  $strip; +} + +sub pkg_change_link { +  my $pkg = shift or return ''; +  return qq!<a href="${p}misc/change_pkg.cgi?$pkg->{pkgnum}">!. +         qq!Change package</a>!; +} + +sub pkg_suspend_link { +  my $pkg = shift or return ''; +  return qq!<a href="${p}misc/susp_pkg.cgi?$pkg->{pkgnum}">Suspend</a>!; +} + +sub pkg_unsuspend_link { +  my $pkg = shift or return ''; +  return qq!<a href="${p}misc/unsusp_pkg.cgi?$pkg->{pkgnum}">Unsuspend</a>!; +} + +sub pkg_cancel_link { +  my $pkg = shift or return ''; +  qq!<A HREF="javascript:areyousure('${p}misc/cancel_pkg.cgi?$pkg->{pkgnum}', !. +  qq!'Permanently delete included services and cancel this package?')">!. +  qq!Cancel now</A> | !. +  qq!<A HREF="${p}misc/expire_pkg.cgi?$pkg->{pkgnum}">Cancel later</A>!; +} + +sub pkg_dates_link { +  my $pkg = shift or return ''; +  qq!<A HREF="${p}edit/REAL_cust_pkg.cgi?$pkg->{pkgnum}">Edit dates</A>!; +} + +sub pkg_customize_link { +  my $pkg = shift or return ''; +  my $custnum = shift; +  qq!<A HREF="${p}edit/part_pkg.cgi?keywords=$custnum;clone=$pkg->{pkgpart};!. +  qq!pkgnum=$pkg->{pkgnum}">Customize</A>!; +} + +%> diff --git a/httemplate/view/cust_main/payment_history.html b/httemplate/view/cust_main/payment_history.html new file mode 100644 index 000000000..0f3a98702 --- /dev/null +++ b/httemplate/view/cust_main/payment_history.html @@ -0,0 +1,357 @@ +<% +  my( $cust_main ) = @_; +  my $conf = new FS::Conf; +  my $custnum = $cust_main->custnum; +%> + +<BR><BR><A NAME="history"><FONT SIZE="+2">Payment History</FONT></A><BR> +<A HREF="<%= $p %>edit/cust_pay.cgi?custnum=<%= $custnum %>">Post cash/check payment</A> +| <A HREF="<%= $p %>misc/payment.cgi?payby=CARD;custnum=<%= $custnum %>">Process credit card payment</A> +| <A HREF="<%= $p %>misc/payment.cgi?payby=CHEK;custnum=<%= $custnum %>">Process electronic check (ACH) payment</A> +<BR><A HREF="<%= $p %>edit/cust_credit.cgi?<%= $custnum %>">Post credit</A> +<BR> + +<% +#get payment history +my @history = (); + +#invoices +foreach my $cust_bill ($cust_main->cust_bill) { +  my $pre = ( $cust_bill->owed > 0 ) +              ? '<B><FONT SIZE="+1" COLOR="#FF0000">Open ' +              : ''; +  my $post = ( $cust_bill->owed > 0 ) ? '</FONT></B>' : ''; +  my $invnum = $cust_bill->invnum; +  push @history, { +    'date'   => $cust_bill->_date, +    'desc'   => qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!. $pre. +                "Invoice #$invnum (Balance \$". $cust_bill->owed. ')'. +                $post. '</A>', +    'charge' => $cust_bill->charged, +  }; +} + +#payments (some false laziness w/credits) +foreach my $cust_pay ($cust_main->cust_pay) { + +  my $payby = $cust_pay->payby; + +  my $payinfo; +  if ( $payby eq 'CARD' ) { +    $payinfo = $cust_pay->payinfo_masked; +  } elsif ( $payby eq 'CHEK' && $cust_pay->payinfo =~ /^(\d+)\@(\d+)$/ ) { +    $payinfo = "ABA $2, Acct# $1"; +  } else { +    $payinfo = $cust_pay->payinfo; +  } +  my @cust_bill_pay = $cust_pay->cust_bill_pay; +  my @cust_pay_refund = $cust_pay->cust_pay_refund; + +  my $target = "$payby$payinfo"; +  $payby =~ s/^BILL$/Check #/ if $payinfo; +  $payby =~ s/^CHEK$/Electronic check /; +  $payby =~ s/^BILL$//; +  $payby =~ s/^(CARD|COMP)$/$1 /; +  my $info = $payby ? " ($payby$payinfo)" : ''; + +  my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' ); +  if (    scalar(@cust_bill_pay)   == 0 +       && scalar(@cust_pay_refund) == 0 ) { +    #completely unapplied +    $pre = '<B><FONT COLOR="#FF0000">Unapplied '; +    $post = '</FONT></B>'; +    $apply = qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!. +             $cust_pay->paynum. '">apply</A>)'; +  } elsif (    scalar(@cust_bill_pay)   == 1 +            && scalar(@cust_pay_refund) == 0 +            && $cust_pay->unapplied == 0     ) { +    #applied to one invoice, the usual situation +    $desc = ' applied to Invoice #'. $cust_bill_pay[0]->invnum; +  } elsif (    scalar(@cust_bill_pay)   == 0 +            && scalar(@cust_pay_refund) == 1 +            && $cust_pay->unapplied == 0     ) { +    #applied to one refund +    $desc = ' refunded on '. time2str("%D", $cust_pay_refund[0]->_date); +  } else { +    #complicated +    $desc = '<BR>'; +    foreach my $app ( sort { $a->_date <=> $b->_date } +                           ( @cust_bill_pay, @cust_pay_refund ) ) { +      if ( $app->isa('FS::cust_bill_pay') ) { +        $desc .= '  '. +                 '$'. $app->amount. +                 ' applied to Invoice #'. $app->invnum. +                 '<BR>'; +                 #' on '. time2str("%D", $cust_bill_pay->_date). +      } elsif ( $app->isa('FS::cust_pay_refund') ) { +        $desc .= '  '. +                 '$'. $app->amount. +                 ' refunded on'. time2str("%D", $app->_date). +                 '<BR>'; +      } else { +        die "$app is not a FS::cust_bill_pay or FS::cust_pay_refund"; +      } +    } +    if ( $cust_pay->unapplied > 0 ) { +      $desc .= '  '. +               '<B><FONT COLOR="#FF0000">$'. +               $cust_pay->unapplied. ' unapplied</FONT></B>'. +               qq! (<A HREF="${p}edit/cust_bill_pay.cgi?!. +               $cust_pay->paynum. '">apply</A>)'. +               '<BR>'; +    } +  } + +  my $refund = ''; +  my $refund_days = $conf->config('card_refund-days') || 120; +  if (    $cust_pay->closed !~ /^Y/i +       && $cust_pay->payby =~ /^(CARD|CHEK)$/ +       && time-$cust_pay->_date < $refund_days*86400 +       && $cust_pay->unrefunded > 0 +  ) { +    $refund = qq! (<A HREF="${p}edit/cust_refund.cgi?payby=$1;!. +              qq!paynum=!. $cust_pay->paynum. '"'. +              qq! TITLE="Send a refund for this payment to the payment gateway"!. +              qq!>refund</A>)!; +  } + +  my $void = ''; +  if (    $cust_pay->closed !~ /^Y/i +       && $cust_pay->payby ne 'CARD' +       && ( $cust_pay->payby ne 'CHEK' || $conf->exists('echeck-void') )  +     ) { +    $void = qq! (<A HREF="javascript:areyousure('!. +            qq!${p}misc/void-cust_pay.cgi?!. $cust_pay->paynum. +            qq!', 'Are you sure you want to void this payment?')"!. +            qq! TITLE="Void this payment from the database!. +              ( $cust_pay->payby eq 'CHEK' +                ? ' (do not send anything to the payment gateway)' +                : ''  +              ). '"'. +            qq!>void</A>)!; +  } + +  my $delete = ''; +  if ( $cust_pay->closed !~ /^Y/i && $conf->exists('deletepayments') ) { +    $delete = qq! (<A HREF="javascript:areyousure('!. +              qq!${p}misc/delete-cust_pay.cgi?!. $cust_pay->paynum. +              qq!', 'Are you sure you want to delete this payment?')"!. +              qq! TITLE="Delete this payment from the database completely - not recommended"!. +              qq!>delete</A>)!; +  } + +  my $unapply = ''; +  if (    $cust_pay->closed !~ /^Y/i +       && $conf->exists('unapplypayments') +       && scalar(@cust_bill_pay)           ) { +    $unapply = qq! (<A HREF="javascript:areyousure('!. +               qq!${p}misc/unapply-cust_pay.cgi?!. $cust_pay->paynum. +               qq!', 'Are you sure you want to unapply this payment?')"!. +               qq! TITLE="Keep this payment, but dissociate it from the invoices it is currently applied against"!. +               qq!>unapply</A>)!; +  } + +  push @history, { +    'date'    => $cust_pay->_date, +    'desc'    => $pre. "Payment$post$info$desc". +                 "$apply$refund$void$delete$unapply", +    'payment' => $cust_pay->paid, +    'target'  => $target, +  }; +} + +#voided payments +foreach my $cust_pay_void ($cust_main->cust_pay_void) { + +  my $payby = $cust_pay_void->payby; +  my $payinfo = $payby eq 'CARD' +                  ? $cust_pay_void->payinfo_masked +                  : $cust_pay_void->payinfo; + +  $payby =~ s/^BILL$/Check #/ if $payinfo; +  $payby =~ s/^CHEK$/Electronic check /; +  $payby =~ s/^BILL$//; +  $payby =~ s/^(CARD|COMP)$/$1 /; +  my $info = $payby ? " ($payby$payinfo)" : ''; + +  push @history, { +    'date'   => $cust_pay_void->_date, +    'desc'   => "<DEL>Payment $info</DEL> <I>voided ". +                time2str("%D", $cust_pay_void->void_date). +                " by ". $cust_pay_void->otaker. '</i>', +    'void_payment' => $cust_pay_void->paid, +  }; + +} + +#credits (some false laziness w/payments) +foreach my $cust_credit ($cust_main->cust_credit) { + +  my @cust_credit_bill = $cust_credit->cust_credit_bill; +  my @cust_credit_refund = $cust_credit->cust_credit_refund; + +  my( $pre, $post, $desc, $apply, $ext ) = ( '', '', '', '', '' ); +  if (    scalar(@cust_credit_bill)   == 0 +       && scalar(@cust_credit_refund) == 0 ) { +    #completely unapplied +    $pre = '<B><FONT COLOR="#FF0000">Unapplied '; +    $post = '</FONT></B>'; +    $apply = qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!. +             $cust_credit->crednum. '">apply</A>)'; +  } elsif (    scalar(@cust_credit_bill)   == 1 +            && scalar(@cust_credit_refund) == 0 +            && $cust_credit->credited == 0      ) { +    #applied to one invoice, the usual situation +    $desc = ' applied to Invoice #'. $cust_credit_bill[0]->invnum; +  } elsif (    scalar(@cust_credit_bill)   == 0 +            && scalar(@cust_credit_refund) == 1 +            && $cust_credit->credited == 0      ) { +    #applied to one refund +    $desc = ' refunded on '.  time2str("%D", $cust_credit_refund[0]->_date); +  } else { +    #complicated +    $desc = '<BR>'; +    foreach my $app ( sort { $a->_date <=> $b->_date } +                           ( @cust_credit_bill, @cust_credit_refund ) ) { +      if ( $app->isa('FS::cust_credit_bill') ) { +        $desc .= '  '. +                 '$'. $app->amount. +                 ' applied to Invoice #'. $app->invnum. +                 '<BR>'; +                 #' on '. time2str("%D", $app->_date). +      } elsif ( $app->isa('FS::cust_credit_refund') ) { +        $desc .= '  '. +                 '$'. $app->amount. +                 ' refunded on'. time2str("%D", $app->_date). +                 '<BR>'; +      } else { +        die "$app is not a FS::cust_credit_bill or a FS::cust_credit_refund"; +      } +    } +    if ( $cust_credit->credited > 0 ) { +      $desc .= '  <B><FONT COLOR="#FF0000">$'. +               $cust_credit->credited. ' unapplied</FONT></B>'. +               qq! (<A HREF="${p}edit/cust_credit_bill.cgi?!. +               $cust_credit->crednum. '">apply</A>)'. +               '<BR>'; +    } +  } +# +  my $delete = ''; +  if ( $cust_credit->closed !~ /^Y/i && $conf->exists('deletecredits') ) { +    $delete = qq! (<A HREF="javascript:areyousure('!. +              qq!${p}misc/delete-cust_credit.cgi?!. $cust_credit->crednum. +              qq!', 'Are you sure you want to delete this credit?')">!. +              qq!delete</A>)!; +  } +   +  my $unapply = ''; +  if (    $cust_credit->closed !~ /^Y/i +       && $conf->exists('unapplycredits') +       && scalar(@cust_credit_bill)       ) { +    $unapply = qq! (<A HREF="javascript:areyousure('!. +               qq!${p}misc/unapply-cust_credit.cgi?!. $cust_credit->crednum. +               qq!', 'Are you sure you want to unapply this credit?')">!. +               qq!unapply</A>)!; +  } +   +  push @history, { +    'date'   => $cust_credit->_date, +    'desc'   => $pre. "Credit$post by ". $cust_credit->otaker. +                ' ('. $cust_credit->reason. ')'. +                "$desc$apply$delete$unapply", +    'credit' => $cust_credit->amount, +  }; + +} + +#refunds +foreach my $cust_refund ($cust_main->cust_refund) { + +  my $payby = $cust_refund->payby; +  my $payinfo = $payby eq 'CARD' +                  ? $cust_refund->payinfo_masked +                  : $cust_refund->payinfo; + +  $payby =~ s/^BILL$/Check #/ if $payinfo; +  $payby =~ s/^CHEK$/Electronic check /; +  $payby =~ s/^(CARD|COMP)$/$1 /; + +  push @history, { +    'date'   => $cust_refund->_date, +    'desc'   => "Refund ($payby$payinfo) by ". $cust_refund->otaker, +    'refund' => $cust_refund->refund, +  }; + +} + +%> + +<%= table() %> +<TR> +  <TH>Date</TH> +  <TH>Description</TH> +  <TH><FONT SIZE=-1>Charge</FONT></TH> +  <TH><FONT SIZE=-1>Payment</FONT></TH> +  <TH><FONT SIZE=-1>In-house<BR>Credit</FONT></TH> +  <TH><FONT SIZE=-1>Refund</FONT></TH> +  <TH><FONT SIZE=-1>Balance</FONT></TH> +</TR> + +<% +#display payment history + +my %target; +my $balance = 0; +foreach my $item ( sort { $a->{'date'} <=> $b->{'date'} } @history ) { + +  my $charge  = exists($item->{'charge'}) +                  ? sprintf('$%.2f', $item->{'charge'}) +                  : ''; +  my $payment = exists($item->{'payment'}) +                  ? sprintf('- $%.2f', $item->{'payment'}) +                  : ''; +  $payment ||= sprintf('<DEL>- $%.2f</DEL>', $item->{'void_payment'}) +    if exists($item->{'void_payment'}); +  my $credit  = exists($item->{'credit'}) +                  ? sprintf('- $%.2f', $item->{'credit'}) +                  : ''; +  my $refund  = exists($item->{'refund'}) +                  ? sprintf('$%.2f', $item->{'refund'}) +                  : ''; + +  my $target = exists($item->{'target'}) ? $item->{'target'} : ''; + +  $balance += $item->{'charge'}  if exists $item->{'charge'}; +  $balance -= $item->{'payment'} if exists $item->{'payment'}; +  $balance -= $item->{'credit'}  if exists $item->{'credit'}; +  $balance += $item->{'refund'}  if exists $item->{'refund'}; +  $balance = sprintf("%.2f", $balance); +  $balance =~ s/^\-0\.00$/0.00/; #yay ieee fp +  ( my $showbalance = '$'. $balance ) =~ s/^\$\-/- \$/; + +%> + +  <TR> +    <TD> +      <% unless ( !$target || $target{$target}++ ) { %> +        <A NAME="<%= $target %>"> +      <% } %> +      <%= time2str("%D",$item->{'date'}) %> +      <% if ( $target && $target{$target} == 1 ) { %> +        </A> +      <% } %> +      </FONT> +    </TD> +    <TD><%= $item->{'desc'} %></TD> +    <TD ALIGN="right"><%= $charge  %></TD> +    <TD ALIGN="right"><%= $payment %></TD> +    <TD ALIGN="right"><%= $credit  %></TD> +    <TD ALIGN="right"><%= $refund  %></TD> +    <TD ALIGN="right"><%= $showbalance %></TD> +  </TR> + +<% } %> + +</TABLE> + diff --git a/httemplate/view/cust_main/quick-charge.html b/httemplate/view/cust_main/quick-charge.html new file mode 100644 index 000000000..e62949e56 --- /dev/null +++ b/httemplate/view/cust_main/quick-charge.html @@ -0,0 +1,32 @@ +<% +  my( $cust_main ) = @_; +  my $conf = new FS::Conf; +%> + +<FORM ACTION="${p}edit/process/quick-charge.cgi" METHOD="POST"> +<INPUT TYPE="hidden" NAME="custnum" VALUE="<%= $cust_main->custnum %>"> +Description:<INPUT TYPE="text" NAME="pkg"> + Amount:<INPUT TYPE="text" NAME="amount" SIZE=6> + <%  +   +  #false laziness w/ edit/part_pkg.cgi +  if ( $conf->exists('enable_taxclasses') ) { +    print '<SELECT NAME="taxclass">'; +    my $sth = dbh->prepare('SELECT DISTINCT taxclass FROM cust_main_county') +      or die dbh->errstr; +    $sth->execute or die $sth->errstr; +    foreach my $taxclass ( map $_->[0], @{$sth->fetchall_arrayref} ) { +      print qq!<OPTION VALUE="$taxclass"!; +      #print ' SELECTED' if $taxclass eq $hashref->{taxclass}; +      print qq!>$taxclass</OPTION>!; +    } +    print '</SELECT>'; +  } else { +    print '<INPUT TYPE="hidden" NAME="taxclass" VALUE="">'; +  } + +%> +   +<INPUT TYPE="submit" VALUE="One-time charge"> +</FORM> + | 
