make net receipts clickable... and netreceipts != cashflow, really, so separate those...
authorivan <ivan>
Mon, 19 May 2008 04:07:02 +0000 (04:07 +0000)
committerivan <ivan>
Mon, 19 May 2008 04:07:02 +0000 (04:07 +0000)
13 files changed:
FS/FS/Report/Table/Monthly.pm
FS/FS/cust_bill_pay.pm
FS/FS/cust_credit_refund.pm
FS/FS/cust_refund.pm
FS/FS/payby.pm
FS/FS/payinfo_Mixin.pm
httemplate/graph/money_time.cgi
httemplate/search/cust_bill_pay.html [new file with mode: 0644]
httemplate/search/cust_credit.html
httemplate/search/cust_credit_refund.html [new file with mode: 0644]
httemplate/search/cust_pay.cgi
httemplate/search/cust_refund.html [new file with mode: 0644]
httemplate/search/elements/cust_pay_or_refund.html [new file with mode: 0755]

index 6daa026..d75f0be 100644 (file)
@@ -1,7 +1,7 @@
 package FS::Report::Table::Monthly;
 
 use strict;
-use vars qw( @ISA $expenses_kludge );
+use vars qw( @ISA );
 use Time::Local;
 use FS::UID qw( dbh );
 use FS::Report::Table;
@@ -9,8 +9,6 @@ use FS::CurrentUser;
 
 @ISA = qw( FS::Report::Table );
 
-$expenses_kludge = 0;
-
 =head1 NAME
 
 FS::Report::Table::Monthly - Tables of report data, indexed monthly
@@ -142,70 +140,24 @@ sub invoiced { #invoiced
 sub netsales { #net sales
   my( $self, $speriod, $eperiod, $agentnum ) = @_;
 
-  my $credited = $self->scalar_sql("
-    SELECT SUM(cust_credit_bill.amount)
-      FROM cust_credit_bill
-        LEFT JOIN cust_bill USING ( invnum  )
-        LEFT JOIN cust_main USING ( custnum )
-    WHERE ".  $self->in_time_period_and_agent( $speriod,
-                                               $eperiod,
-                                               $agentnum,
-                                               'cust_bill._date'
-                                             )
-  );
-
-  #horrible local kludge
-  my $expenses = !$expenses_kludge ? 0 : $self->scalar_sql("
-    SELECT SUM(cust_bill_pkg.setup)
-      FROM cust_bill_pkg
-        LEFT JOIN cust_bill USING ( invnum  )
-        LEFT JOIN cust_main USING ( custnum )
-        LEFT JOIN cust_pkg  USING ( pkgnum  )
-        LEFT JOIN part_pkg  USING ( pkgpart )
-      WHERE ". $self->in_time_period_and_agent( $speriod,
-                                                $eperiod,
-                                                $agentnum,
-                                                'cust_bill._date'
-                                              ). "
-        AND LOWER(part_pkg.pkg) LIKE 'expense _%'
-  ");
-
-  $self->invoiced($speriod,$eperiod,$agentnum) - $credited - $expenses;
+    $self->invoiced($speriod,$eperiod,$agentnum)
+  - $self->credits( $speriod,$eperiod,$agentnum);
 }
 
 #deferred revenue
 
-sub receipts { #cashflow
+sub cashflow {
   my( $self, $speriod, $eperiod, $agentnum ) = @_;
 
-  my $refunded = $self->scalar_sql("
-    SELECT SUM(refund)
-      FROM cust_refund
-        LEFT JOIN cust_main USING ( custnum )
-      WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum)
-  );
+    $self->payments($speriod, $eperiod, $agentnum)
+  - $self->refunds( $speriod, $eperiod, $agentnum);
+}
 
-  #horrible local kludge that doesn't even really work right
-  my $expenses = !$expenses_kludge ? 0 : $self->scalar_sql("
-    SELECT SUM(cust_bill_pay.amount)
-      FROM cust_bill_pay
-        LEFT JOIN cust_bill USING ( invnum  )
-        LEFT JOIN cust_main USING ( custnum )
-    WHERE ". $self->in_time_period_and_agent( $speriod,
-                                              $eperiod,
-                                              $agentnum,
-                                              'cust_bill_pay._date'
-                                            ). "
-    AND 0 < ( SELECT COUNT(*) from cust_bill_pkg, cust_pkg, part_pkg
-              WHERE cust_bill.invnum = cust_bill_pkg.invnum
-              AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum
-              AND cust_pkg.pkgpart = part_pkg.pkgpart
-              AND LOWER(part_pkg.pkg) LIKE 'expense _%'
-            )
-  ");
-  #    my $expenses_sql2 = "SELECT SUM(cust_bill_pay.amount) FROM cust_bill_pay, cust_bill_pkg, cust_bill, cust_pkg, part_pkg WHERE cust_bill_pay.invnum = cust_bill.invnum AND cust_bill.invnum = cust_bill_pkg.invnum AND cust_bill_pay._date >= $speriod AND cust_bill_pay._date < $eperiod AND cust_pkg.pkgnum = cust_bill_pkg.pkgnum AND cust_pkg.pkgpart = part_pkg.pkgpart AND LOWER(part_pkg.pkg) LIKE 'expense _%'";
-  
-  $self->payments($speriod, $eperiod, $agentnum) - $refunded - $expenses;
+sub netcashflow {
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+
+    $self->receipts($speriod, $eperiod, $agentnum)
+  - $self->netrefunds( $speriod, $eperiod, $agentnum);
 }
 
 sub payments {
@@ -228,6 +180,16 @@ sub credits {
   );
 }
 
+sub refunds {
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+  $self->scalar_sql("
+    SELECT SUM(refund)
+      FROM cust_refund
+        LEFT JOIN cust_main USING ( custnum )
+      WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum)
+  );
+}
+
 sub netcredits {
   my( $self, $speriod, $eperiod, $agentnum ) = @_;
   $self->scalar_sql("
@@ -243,6 +205,36 @@ sub netcredits {
   );
 }
 
+sub receipts { #net payments
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+  $self->scalar_sql("
+    SELECT SUM(cust_bill_pay.amount)
+      FROM cust_bill_pay
+        LEFT JOIN cust_bill USING ( invnum  )
+        LEFT JOIN cust_main USING ( custnum )
+      WHERE ". $self->in_time_period_and_agent( $speriod,
+                                                $eperiod,
+                                                $agentnum,
+                                                'cust_bill._date'
+                                              )
+  );
+}
+
+sub netrefunds {
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+  $self->scalar_sql("
+    SELECT SUM(cust_credit_refund.amount)
+      FROM cust_credit_refund
+        LEFT JOIN cust_credit USING ( crednum  )
+        LEFT JOIN cust_main   USING ( custnum )
+      WHERE ". $self->in_time_period_and_agent( $speriod,
+                                                $eperiod,
+                                                $agentnum,
+                                                'cust_credit._date'
+                                              )
+  );
+}
+
 #these should be auto-generated or $AUTOLOADed or something
 sub invoiced_12mo {
   my( $self, $speriod, $eperiod, $agentnum ) = @_;
@@ -280,6 +272,31 @@ sub netcredits_12mo {
   $self->netcredits($speriod, $eperiod, $agentnum);
 }
 
+sub cashflow_12mo {
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+  $speriod = $self->_subtract_11mo($speriod);
+  $self->cashflow($speriod, $eperiod, $agentnum);
+}
+
+sub netcashflow_12mo {
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+  $speriod = $self->_subtract_11mo($speriod);
+  $self->cashflow($speriod, $eperiod, $agentnum);
+}
+
+sub refunds_12mo {
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+  $speriod = $self->_subtract_11mo($speriod);
+  $self->refunds($speriod, $eperiod, $agentnum);
+}
+
+sub netrefunds_12mo {
+  my( $self, $speriod, $eperiod, $agentnum ) = @_;
+  $speriod = $self->_subtract_11mo($speriod);
+  $self->netrefunds($speriod, $eperiod, $agentnum);
+}
+
+
 #not being too bad with the false laziness
 use Time::Local qw(timelocal);
 sub _subtract_11mo {
index 74a8bcd..b7ba2b7 100644 (file)
@@ -3,11 +3,12 @@ package FS::cust_bill_pay;
 use strict;
 use vars qw( @ISA $conf );
 use FS::Record qw( qsearchs );
+use FS::cust_main_Mixin;
 use FS::cust_bill_ApplicationCommon;
 use FS::cust_bill;
 use FS::cust_pay;
 
-@ISA = qw( FS::cust_bill_ApplicationCommon );
+@ISA = qw( FS::cust_main_Mixin FS::cust_bill_ApplicationCommon );
 
 #ask FS::UID to run this stuff for us later
 FS::UID->install_callback( sub { 
index f237efe..9fc03f2 100644 (file)
@@ -3,11 +3,11 @@ package FS::cust_credit_refund;
 use strict;
 use vars qw( @ISA );
 use FS::Record qw( qsearch qsearchs dbh );
-#use FS::UID qw(getotaker);
+use FS::cust_main_Mixin;
 use FS::cust_credit;
 use FS::cust_refund;
 
-@ISA = qw( FS::Record );
+@ISA = qw( FS::cust_main_Mixin FS::Record );
 
 =head1 NAME
 
index 4981795..e075e3e 100644 (file)
@@ -11,7 +11,7 @@ use FS::cust_pay_refund;
 use FS::cust_main;
 use FS::payinfo_Mixin;
 
-@ISA = qw( FS::Record FS::payinfo_Mixin );
+@ISA = qw( FS::payinfo_Mixin FS::cust_main_Mixin FS::Record );
 
 @encrypted_fields = ('payinfo');
 
@@ -216,7 +216,7 @@ sub delete {
 
 =item replace OLD_RECORD
 
-Currently unimplemented (accounting reasons).
+Modifying a refund?  Well, don't say I didn't warn you.
 
 =cut
 
index 6684c95..349d266 100644 (file)
@@ -124,7 +124,7 @@ sub can_payby {
   #return "Illegal payby" unless $hash{$payby};
   return 0 unless $hash{$payby};
 
-  $table = 'cust_pay' if $table eq 'cust_pay_batch' || $table eq 'cust_refund';
+  $table = 'cust_pay' if $table =~ /^cust_(pay_pending|pay_batch|pay_void|refund)$/;
   return 0 if exists( $hash{$payby}->{$table} );
 
   return 1;
index bdfe6c9..fb56371 100644 (file)
@@ -22,9 +22,7 @@ This class handles the following functions for payinfo...
 
 Payment Mask (Generation and Storage)
 Data Validation (parent checks need to be sure to call this)
-Encryption - In the Future (Pull from Record.pm)
-Bad Card Stuff - In the Future (Integrate Banned Pay)
-Currency - In the Future
+Pretty printing
 
 =head1 FIELDS
 
@@ -182,11 +180,6 @@ sub mask_payinfo {
   return $paymask;
 }
 
-=cut
-
-sub _mask_payinfo {
-  my $self = shift;
-
 =item payinfo_check
 
 Checks payby and payinfo.
@@ -244,9 +237,46 @@ sub payinfo_check {
 
 }
 
+=item payby_payinfo_pretty
+
+Returns payment method and information (suitably masked, if applicable) as
+a human-readable string, such as:
+
+  Card #54xxxxxxxxxxxx32
+
+or
+
+  Check #119006
+
+=cut
+
+sub payby_payinfo_pretty {
+  my $self = shift;
+  if ( $self->payby eq 'CARD' ) {
+    'Card #'. $self->paymask;
+  } elsif ( $self->payby eq 'CHEK' ) {
+    'E-check acct#'. $self->payinfo;
+  } elsif ( $self->payby eq 'BILL' ) {
+    'Check #'. $self->payinfo;
+  } elsif ( $self->payby eq 'PREP' ) {
+    'Prepaid card #'. $self->payinfo;
+  } elsif ( $self->payby eq 'CASH' ) {
+    'Cash '. $self->payinfo;
+  } elsif ( $self->payby eq 'WEST' ) {
+    'Western Union'; #. $self->payinfo;
+  } elsif ( $self->payby eq 'MCRD' ) {
+    'Manual credit card'; #. $self->payinfo;
+  } else {
+    $self->payby. ' '. $self->payinfo;
+  }
+}
+
 =head1 BUGS
 
-Have to add the future items...
+Future items?
+  Encryption - In the Future (Pull from Record.pm)
+  Bad Card Stuff - In the Future (Integrate Banned Pay)
+  Currency - In the Future
 
 =head1 SEE ALSO
 
index b996380..fbc31c0 100644 (file)
@@ -25,27 +25,40 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
 
 my $agentname = $agent ? $agent->agent.' ' : '';
 
-my @items = qw( invoiced netsales credits netcredits payments receipts );
+my @items = qw( invoiced netsales
+                credits  netcredits
+                payments receipts
+                refunds  netrefunds
+                cashflow netcashflow
+              );
 if ( $cgi->param('12mo') == 1 ) {
   @items = map $_.'_12mo', @items;
 }
 
 my %label = (
-  'invoiced'   => 'Gross Sales',
-  'netsales'   => 'Net Sales',
-  'credits'    => 'Gross Credits',
-  'netcredits' => 'Net Credits',
-  'payments'   => 'Gross Receipts',
-  'receipts'   => 'Net Receipts',
+  'invoiced'    => 'Gross Sales',
+  'netsales'    =>   'Net Sales',
+  'credits'     => 'Gross Credits',
+  'netcredits'  =>   'Net Credits',
+  'payments'    => 'Gross Receipts',
+  'receipts'    =>   'Net Receipts',
+  'refunds'     => 'Gross Refunds',
+  'netrefunds'  =>   'Net Refunds',
+  'cashflow'    => 'Gross Cashflow',
+  'netcashflow' =>   'Net Cashflow',
 );
 
 my %graph_suffix = (
- 'invoiced'   => ' (invoiced)', 
- 'netsales'   => ' (invoiced - applied credits)',
- 'credits'    => ' (credited)',
- 'netcredits' => ' (applied credits)',
- 'payments'   => ' (payments)',
- 'receipts'   => '/Cashflow (payments - refunds)',
+ 'invoiced'    => ' (invoiced)', 
+ 'netsales'    => ' (invoiced - applied credits)',
+ 'credits'     => ' (credited)',
+ 'netcredits'  => ' (applied credits)',
+ 'payments'    => ' (payments)',
+ 'receipts'    => ' (applied payments)',
+ 'refunds'     => ' (refunds)',
+ 'netrefunds'  => ' (applied refunds)',
+ 'cashflow'    => ' (payments - refunds)',
+ 'netcashflow' => ' (applied payments - applied refunds)',
 );
 my %graph_label = map { $_ => $label{$_}.$graph_suffix{$_} } keys %label;
 
@@ -56,12 +69,16 @@ $graph_label{$_.'_12mo'} = $graph_label{$_}. " (previous 12 months)"
   foreach keys %graph_label;
 
 my %color = (
-  'invoiced'   => '9999ff', #light blue
-  'netsales'   => '0000cc', #blue
-  'credits'    => 'ff9999', #light red
-  'netcredits' => 'cc0000', #red
-  'payments'   => '99cc99', #light green
-  'receipts'   => '00cc00', #green
+  'invoiced'    => '9999ff', #light blue
+  'netsales'    => '0000cc', #blue
+  'credits'     => 'ff9999', #light red
+  'netcredits'  => 'cc0000', #red
+  'payments'    => '99cc99', #light green
+  'receipts'    => '00cc00', #green
+  'refunds'     => 'ffcc99', #light orange
+  'netrefunds'  => 'ff9900', #orange
+  'cashflow'    => '99cc33', #light olive
+  'netcashflow' => '339900', #olive
 );
 $color{$_.'_12mo'} = $color{$_}
   foreach keys %color;
@@ -72,6 +89,9 @@ my %link = (
   'credits'    => "${p}search/cust_credit.html?agentnum=$agentnum;",
   'netcredits' => "${p}search/cust_credit_bill.html?agentnum=$agentnum;",
   'payments'   => "${p}search/cust_pay.cgi?magic=_date;agentnum=$agentnum;",
+  'receipts'   => "${p}search/cust_bill_pay.html?agentnum=$agentnum;",
+  'refunds'    => "${p}search/cust_refund.html?magic=_date;agentnum=$agentnum;",
+  'netrefunds' => "${p}search/cust_credit_refund.html?agentnum=$agentnum;",
 );
 # XXX link 12mo?
 
diff --git a/httemplate/search/cust_bill_pay.html b/httemplate/search/cust_bill_pay.html
new file mode 100644 (file)
index 0000000..3c390e7
--- /dev/null
@@ -0,0 +1,131 @@
+<% include( 'elements/search.html',
+                'title'       => $title,
+                'name'        => 'net payments',
+                'query'       => $sql_query,
+                'count_query' => $count_query,
+                'count_addl'  => [ '$%.2f total paid (net)', ],
+                'header'      => [ 'Net applied',
+                                   'to Invoice',
+                                   'Payment',
+                                   'By',
+                                   FS::UI::Web::cust_header(),
+                                 ],
+                'fields'      => [
+                   sub { $money_char. sprintf('%.2f', shift->amount ) },
+                   sub { my $cbp = shift;
+                         '#'.$cbp->invnum. ' '.
+                         time2str('%b %d %Y', $cbp->cust_bill_date ).
+                         " ($money_char".
+                           sprintf('%.2f', $cbp->cust_bill_amount).
+                         ")" 
+                       },
+                   sub { my $cbp = shift;
+                         $cbp->cust_pay->payby_payinfo_pretty. ' '.
+                         time2str('%b %d %Y', $cbp->_date ).
+                         " ($money_char".
+                         sprintf('%.2f', $cbp->cust_pay_paid ).
+                         ")"
+                       },
+                   sub { shift->cust_pay->otaker },
+                   \&FS::UI::Web::cust_fields,
+                ],
+                'align' => 'rrrl'.FS::UI::Web::cust_aligns(),
+                'links' => [
+                             '',
+                             $cust_bill_link,
+                             $cust_pay_link,
+                             '',
+                             ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
+                                   FS::UI::Web::cust_header()
+                             ),
+                           ],
+                'color' => [ 
+                             '',
+                             '',
+                             '',
+                             '',
+                             FS::UI::Web::cust_colors(),
+                           ],
+                'style' => [ 
+                             '',
+                             '',
+                             '',
+                             '',
+                             FS::UI::Web::cust_styles(),
+                           ],
+          )
+%>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $title = 'Net Payment Search Results';
+
+my @search = ();
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+  push @search, "agentnum = $1";
+  my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+  die "unknown agentnum $1" unless $agent;
+  $title = $agent->agent. " $title";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "cust_bill._date >= $beginning ",
+              "cust_bill._date <= $ending";
+
+#here is the agent virtualization
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $where = 'WHERE '. join(' AND ', @search);
+#
+my $count_query = 'SELECT COUNT(*), SUM(amount)
+                   FROM cust_bill_pay
+                     LEFT JOIN cust_bill USING ( invnum  )
+                     LEFT JOIN cust_main USING ( custnum ) '.
+                  $where;
+
+my $sql_query   = {
+  'table'     => 'cust_bill_pay',
+  'select'    => join(', ',
+                   'cust_bill_pay.*',
+                   'cust_pay.paid       AS cust_pay_paid',
+                   'cust_bill._date     AS cust_bill_date',
+                   #'cust_bill.charged   AS cust_bill_charged',
+                   'cust_pay.custnum    AS custnum',
+                   'cust_main.custnum   AS cust_main_custnum',
+                   FS::UI::Web::cust_sql_fields(),
+                 ),
+  'hashref'   => {},
+  'extra_sql' => $where,
+  'addl_from' => 'LEFT JOIN cust_bill   USING ( invnum  )
+                  LEFT JOIN cust_pay    USING ( paynum )
+                  LEFT JOIN cust_main ON ( cust_bill.custnum = cust_main.custnum )',
+};
+
+my $cust_bill_link = sub {
+  my $cust_bill_pay = shift;
+  $cust_bill_pay->invnum
+    ? [ "${p}view/cust_bill.cgi?", 'invnum' ]
+    : '';
+};
+
+my $cust_pay_link = sub {
+  my $cust_bill_pay = shift;
+  $cust_bill_pay->paynum
+    ? [ "${p}view/cust_pay.html?paynum=", 'paynum' ]
+    : '';
+};
+
+my $cust_link = sub {
+  my $cust_credit_bill = shift;
+  $cust_credit_bill->cust_main_custnum
+    ? [ "${p}view/cust_main.cgi?", 'cust_main_custnum' ]
+    : '';
+};
+
+</%init>
index 2c59297..9a14dce 100755 (executable)
@@ -7,42 +7,42 @@
                  #'redirect'    => $link,
                  'header'      => [ 'Amount',
                                     'Date',
-                                    FS::UI::Web::cust_header(),
                                     'By',
-                                    'Reason'
+                                    'Reason',
+                                    FS::UI::Web::cust_header(),
                                   ],
                  'fields'      => [
                    #'crednum',
                    sub { sprintf('$%.2f', shift->amount ) },
                    sub { time2str('%b %d %Y', shift->_date ) },
-                   \&FS::UI::Web::cust_fields,
                    'otaker',
                    'reason',
+                   \&FS::UI::Web::cust_fields,
                  ],
                  #'align' => 'rrrllll',
-                 'align' => 'rr'.FS::UI::Web::cust_aligns().'ll',
+                 'align' => 'rrll'.FS::UI::Web::cust_aligns(),
                  'links' => [
                    '',
                    '',
+                   '',
+                   '',
                    ( map { $_ ne 'Cust. Status' ? $clink : '' }
                          FS::UI::Web::cust_header()
                    ),
-                   '',
-                   '',
                  ],
                  'color' => [ 
                               '',
                               '',
-                              FS::UI::Web::cust_colors(),
                               '',
                               '',
+                              FS::UI::Web::cust_colors(),
                             ],
                  'style' => [ 
                               '',
                               '',
-                              FS::UI::Web::cust_styles(),
                               '',
                               '',
+                              FS::UI::Web::cust_styles(),
                             ],
       )
 %>
diff --git a/httemplate/search/cust_credit_refund.html b/httemplate/search/cust_credit_refund.html
new file mode 100644 (file)
index 0000000..d9abe2e
--- /dev/null
@@ -0,0 +1,130 @@
+<% include( 'elements/search.html',
+                'title'       => $title,
+                'name'        => 'net refunds',
+                'query'       => $sql_query,
+                'count_query' => $count_query,
+                'count_addl'  => [ '$%.2f total refunded (net)', ],
+                'header'      => [ 'Net applied',
+                                   'to Credit',
+                                   'Refund',
+                                   'By',
+                                   FS::UI::Web::cust_header(),
+                                 ],
+               'fields'      => [
+                   sub { $money_char. sprintf('%.2f', shift->amount ) },
+                   sub { my $ccr = shift;
+                         '#'.$ccr->crednum. ' '.
+                         time2str('%b %d %Y', $ccr->cust_credit_date ).
+                         " ($money_char".
+                           sprintf('%.2f', $ccr->cust_credit_amount).
+                         ")" 
+                       },
+                   sub { my $ccr = shift;
+                         time2str('%b %d %Y', $ccr->_date ).
+                         " ($money_char".
+                         sprintf('%.2f', $ccr->cust_refund_refund ).
+                         ")"
+                       },
+                   sub { shift->cust_refund->otaker },
+                   \&FS::UI::Web::cust_fields,
+                ],
+                'align' => 'rrrl'.FS::UI::Web::cust_aligns(),
+                'links' => [
+                             '',
+                             '',
+                             '',
+                             '',
+                             ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
+                                   FS::UI::Web::cust_header()
+                             ),
+                           ],
+                'color' => [ 
+                             '',
+                             '',
+                             '',
+                             '',
+                             FS::UI::Web::cust_colors(),
+                           ],
+                'style' => [ 
+                             '',
+                             '',
+                             '',
+                             '',
+                             FS::UI::Web::cust_styles(),
+                           ],
+          )
+%>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $title = 'Net Refund Search Results';
+
+my @search = ();
+
+if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+  push @search, "agentnum = $1";
+  my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+  die "unknown agentnum $1" unless $agent;
+  $title = $agent->agent. " $title";
+}
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @search, "cust_credit._date >= $beginning ",
+              "cust_credit._date <= $ending";
+
+#here is the agent virtualization
+push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+my $where = 'WHERE '. join(' AND ', @search);
+#
+my $count_query = 'SELECT COUNT(*), SUM(cust_credit_refund.amount)
+                   FROM cust_credit_refund
+                     LEFT JOIN cust_credit USING ( crednum )
+                     LEFT JOIN cust_main   USING ( custnum ) '.
+                  $where;
+
+my $sql_query   = {
+  'table'     => 'cust_credit_refund',
+  'select'    => join(', ',
+                   'cust_credit_refund.*',
+                   'cust_refund.refund  AS cust_refund_refund',
+                   'cust_credit._date   AS cust_credit_date',
+                   'cust_credit.amount  AS cust_credit_amnount',
+                   'cust_refund.custnum AS custnum',
+                   'cust_main.custnum   AS cust_main_custnum',
+                   FS::UI::Web::cust_sql_fields(),
+                 ),
+  'hashref'   => {},
+  'extra_sql' => $where,
+  'addl_from' => 'LEFT JOIN cust_credit USING ( crednum   )
+                  LEFT JOIN cust_refund USING ( refundnum )
+                  LEFT JOIN cust_main ON ( cust_credit.custnum = cust_main.custnum )',
+};
+
+#my $cust_credit_link = sub {
+#  my $cust_credit_refund = shift;
+#  $cust_credit_refund->crednum
+#    ? [ "${p}view/cust_credit.cgi?", 'credum' ]
+#    : '';
+#};
+
+#my $cust_refund_link = sub {
+#  my $cust_credit_refund = shift;
+#  $cust_credit_refund->refundnum
+#    ? [ "${p}view/cust_refund.cgi?", 'refundnum' ]
+#    : '';
+#};
+
+my $cust_link = sub {
+  my $cust_credit_refund = shift;
+  $cust_credit_refund->cust_main_custnum
+    ? [ "${p}view/cust_main.cgi?", 'cust_main_custnum' ]
+    : '';
+};
+
+</%init>
index 80a611d..65bd39e 100755 (executable)
@@ -1,250 +1,7 @@
-<% include( 'elements/search.html',
-                 'title'       => $title,
-                 'name'        => 'payments',
-                 'query'       => $sql_query,
-                 'count_query' => $count_query,
-                 'count_addl'  => [ '$%.2f total paid', ],
-                 'header'      => [ 'Payment',
-                                    'Amount',
-                                    'Date',
-                                    'By',
-                                    FS::UI::Web::cust_header(),
-                                  ],
-                 'fields'      => [
-                   sub {
-                     my $cust_pay = shift;
-                     if ( $cust_pay->payby eq 'CARD' ) {
-                       'Card #'. $cust_pay->paymask;
-                     } elsif ( $cust_pay->payby eq 'CHEK' ) {
-                       'E-check acct#'. $cust_pay->payinfo;
-                     } elsif ( $cust_pay->payby eq 'BILL' ) {
-                       'Check #'. $cust_pay->payinfo;
-                     } elsif ( $cust_pay->payby eq 'PREP' ) {
-                       'Prepaid card #'. $cust_pay->payinfo;
-                     } elsif ( $cust_pay->payby eq 'CASH' ) {
-                       'Cash '. $cust_pay->payinfo;
-                     } elsif ( $cust_pay->payby eq 'WEST' ) {
-                       'Western Union'; #. $cust_pay->payinfo;
-                     } elsif ( $cust_pay->payby eq 'MCRD' ) {
-                       'Manual credit card'; #. $cust_pay->payinfo;
-                     } else {
-                       $cust_pay->payby. ' '. $cust_pay->payinfo;
-                     }
-                   },
-                   sub { sprintf('$%.2f', shift->paid ) },
-                   sub { time2str('%b %d %Y', shift->_date ) },
-                   sub { my $o = shift->otaker;
-                         $o = 'auto billing'          if $o eq 'fs_daily';
-                         $o = 'customer self-service' if $o eq 'fs_selfservice';
-                         $o;
-                       },
-                   \&FS::UI::Web::cust_fields,
-                 ],
-                 #'align' => 'lrrrll',
-                 'align' => 'rrrc'.FS::UI::Web::cust_aligns(),
-                 'links' => [
-                   $link,
-                   $link,
-                   $link,
-                   '',
-                   ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
-                         FS::UI::Web::cust_header()
-                   ),
-                 ],
-                 'color' => [ 
-                              '',
-                              '',
-                              '',
-                              '',
-                              FS::UI::Web::cust_colors(),
-                            ],
-                 'style' => [ 
-                              '',
-                              '',
-                              '',
-                              '',
-                              FS::UI::Web::cust_styles(),
-                            ],
-      )
+<% include( 'elements/cust_pay_or_refund.html',
+                'thing'         => 'pay',
+                'amount_field'  => 'paid',
+                'name_singular' => 'payment',
+                'name_verb'     => 'paid',
+          )
 %>
-<%init>
-
-die "access denied"
-  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
-
-my $title = 'Payment Search Results';
-my( $count_query, $sql_query );
-if ( $cgi->param('magic') ) {
-
-  my @search = ();
-  my $orderby;
-  if ( $cgi->param('magic') eq '_date' ) {
-
-
-    if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
-      push @search, "agentnum = $1"; # $search{'agentnum'} = $1;
-      my $agent = qsearchs('agent', { 'agentnum' => $1 } );
-      die "unknown agentnum $1" unless $agent;
-      $title = $agent->agent. " $title";
-    }
-  
-    if ( $cgi->param('payby') ) {
-      $cgi->param('payby') =~
-        /^(CARD|CHEK|BILL|PREP|CASH|WEST|MCRD)(-(VisaMC|Amex|Discover|Maestro))?$/
-          or die "illegal payby ". $cgi->param('payby');
-      push @search, "cust_pay.payby = '$1'";
-      if ( $3 ) {
-
-        my $cardtype = $3;
-
-        my $search;
-        if ( $cardtype eq 'VisaMC' ) {
-          #avoid posix regexes for portability
-          $search =
-            " ( (     substring(cust_pay.payinfo from 1 for 1) = '4'     ".
-            "     AND substring(cust_pay.payinfo from 1 for 4) != '4936' ".
-            "     AND substring(cust_pay.payinfo from 1 for 6)           ".
-            "         NOT SIMILAR TO '49030[2-9]'                        ".
-            "     AND substring(cust_pay.payinfo from 1 for 6)           ".
-            "         NOT SIMILAR TO '49033[5-9]'                        ".
-            "     AND substring(cust_pay.payinfo from 1 for 6)           ".
-            "         NOT SIMILAR TO '49110[1-2]'                        ".
-            "     AND substring(cust_pay.payinfo from 1 for 6)           ".
-            "         NOT SIMILAR TO '49117[4-9]'                        ".
-            "     AND substring(cust_pay.payinfo from 1 for 6)           ".
-            "         NOT SIMILAR TO '49118[1-2]'                        ".
-            "   )".
-            "   OR substring(cust_pay.payinfo from 1 for 2) = '51' ".
-            "   OR substring(cust_pay.payinfo from 1 for 2) = '52' ".
-            "   OR substring(cust_pay.payinfo from 1 for 2) = '53' ".
-            "   OR substring(cust_pay.payinfo from 1 for 2) = '54' ".
-            "   OR substring(cust_pay.payinfo from 1 for 2) = '54' ".
-            "   OR substring(cust_pay.payinfo from 1 for 2) = '55' ".
-            "   OR substring(cust_pay.payinfo from 1 for 2) = '36' ". #Diner's int'l processed as Visa/MC inside US
-            " ) ";
-        } elsif ( $cardtype eq 'Amex' ) {
-          $search =
-            " (    substring(cust_pay.payinfo from 1 for 2 ) = '34' ".
-            "   OR substring(cust_pay.payinfo from 1 for 2 ) = '37' ".
-            " ) ";
-        } elsif ( $cardtype eq 'Discover' ) {
-          $search =
-            " (    substring(cust_pay.payinfo from 1 for 4 ) = '6011'  ".
-            "   OR substring(cust_pay.payinfo from 1 for 2 ) = '65'    ".
-            "   OR substring(cust_pay.payinfo from 1 for 3 ) = '622'   ". #China Union Pay processed as Discover outside CN
-            " ) ";
-        } elsif ( $cardtype eq 'Maestro' ) { 
-          $search =
-            " (    substring(cust_pay.payinfo from 1 for 2 ) = '63'     ".
-            "   OR substring(cust_pay.payinfo from 1 for 2 ) = '67'     ".
-            "   OR substring(cust_pay.payinfo from 1 for 6 ) = '564182' ".
-            "   OR substring(cust_pay.payinfo from 1 for 4 ) = '4936'   ".
-            "   OR substring(cust_pay.payinfo from 1 for 6 )            ".
-            "      SIMILAR TO '49030[2-9]'                             ".
-            "   OR substring(cust_pay.payinfo from 1 for 6 )            ".
-            "      SIMILAR TO '49033[5-9]'                             ".
-            "   OR substring(cust_pay.payinfo from 1 for 6 )            ".
-            "      SIMILAR TO '49110[1-2]'                             ".
-            "   OR substring(cust_pay.payinfo from 1 for 6 )            ".
-            "      SIMILAR TO '49117[4-9]'                             ".
-            "   OR substring(cust_pay.payinfo from 1 for 6 )            ".
-            "      SIMILAR TO '49118[1-2]'                             ".
-            " ) ";
-        } else {
-          die "unknown card type $cardtype";
-        }
-
-        my $masksearch = $search;
-        $masksearch =~ s/cust_pay\.payinfo/cust_pay.paymask/gi;
-
-        push @search,
-          "( $search OR ( cust_pay.paymask IS NOT NULL AND $masksearch ) )";
-
-      }
-    }
-
-    if ( $cgi->param('payinfo') ) {
-      $cgi->param('payinfo') =~ /^\s*(\d+)\s*$/
-        or die "illegal payinfo ". $cgi->param('payinfo');
-      push @search, "cust_pay.payinfo = '$1'";
-    }
-
-    my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
-    push @search, "_date >= $beginning ",
-                  "_date <= $ending";
-
-    push @search, FS::UI::Web::parse_lt_gt($cgi, 'paid' );
-
-    $orderby = '_date';
-
-  } elsif ( $cgi->param('magic') eq 'paybatch' ) {
-
-    $cgi->param('paybatch') =~ /^([\w\/\:\-\.]+)$/
-      or die "illegal paybatch: ". $cgi->param('paybatch');
-
-    push @search, "paybatch = '$1'";
-
-    $orderby = "LOWER(company || ' ' || last || ' ' || first )";
-
-  } else {
-    die "unknown search magic: ". $cgi->param('magic');
-  }
-
-  #here is the agent virtualization
-  push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
-
-  my $search = ' WHERE '. join(' AND ', @search);
-
-  $count_query = "SELECT COUNT(*), SUM(paid) ".
-                 "FROM cust_pay LEFT JOIN cust_main USING ( custnum )".
-                 $search;
-
-  $sql_query = {
-    'table'     => 'cust_pay',
-    'select'    => join(', ',
-                     'cust_pay.*',
-                     'cust_main.custnum as cust_main_custnum',
-                     FS::UI::Web::cust_sql_fields(),
-                   ),
-    'hashref'   => {},
-    'extra_sql' => "$search ORDER BY $orderby",
-    'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
-  };
-
-} else {
-
-  #hmm... is this still used?
-
-  $cgi->param('payinfo') =~ /^\s*(\d+)\s*$/ or die "illegal payinfo";
-  my $payinfo = $1;
-
-  $cgi->param('payby') =~ /^(\w+)$/ or die "illegal payby";
-  my $payby = $1;
-
-  $count_query = "SELECT COUNT(*), SUM(paid) FROM cust_pay".
-                 "  WHERE payinfo = '$payinfo' AND payby = '$payby'".
-                 "  AND ". $FS::CurrentUser::CurrentUser->agentnums_sql;
-
-  $sql_query = {
-    'table'     => 'cust_pay',
-    'hashref'   => { 'payinfo' => $payinfo,
-                     'payby'   => $payby    },
-    'extra_sql' => $FS::CurrentUser::CurrentUser->agentnums_sql.
-                   " ORDER BY _date",
-  };
-
-}
-
-my $link = '';
-$link = [ "${p}view/cust_pay.html?paynum=", 'paynum' ]
-  if $FS::CurrentUser::CurrentUser->access_right('View invoices'); #XXX for now
-  #later# if $FS::CurrentUser::CurrentUser->access_right('View customer payments');
-
-my $cust_link = sub {
-  my $cust_pay = shift;
-  $cust_pay->cust_main_custnum
-    ? [ "${p}view/cust_main.cgi?", 'custnum' ] 
-    : '';
-};
-
-</%init>
diff --git a/httemplate/search/cust_refund.html b/httemplate/search/cust_refund.html
new file mode 100644 (file)
index 0000000..e31e088
--- /dev/null
@@ -0,0 +1,7 @@
+<% include( 'elements/cust_pay_or_refund.html',
+                'thing'         => 'refund',
+                'amount_field'  => 'refund',
+                'name_singular' => 'refund',
+                'name_verb'     => 'refunded',
+          )
+%>
diff --git a/httemplate/search/elements/cust_pay_or_refund.html b/httemplate/search/elements/cust_pay_or_refund.html
new file mode 100755 (executable)
index 0000000..cd1e19d
--- /dev/null
@@ -0,0 +1,258 @@
+<%doc>
+
+Examples:
+
+  include( 'elements/cust_pay_or_refund.html',
+               'thing'         => 'pay',
+               'amount_field'  => 'paid',
+               'name_singular' => 'payment',
+               'name_verb'     => 'paid',
+         )
+
+  include( 'elements/cust_pay_or_refund.html',
+               'thing'         => 'refund',
+               'amount_field'  => 'refund',
+               'name_singular' => 'refund',
+               'name_verb'     => 'refunded',
+         )
+
+</%doc>
+<% include( 'search.html',
+                'title'         => $title,
+                'name_singular' => $name_singular,
+                'query'         => $sql_query,
+                'count_query'   => $count_query,
+                'count_addl'    => [ '$%.2f total '.$opt{name_verb}, ],
+                'header'        => [ "\u$name_singular",
+                                     'Amount',
+                                     'Date',
+                                     'By',
+                                     FS::UI::Web::cust_header(),
+                                   ],
+                'fields'      => [
+                  'payby_payinfo_pretty',
+                  sub { sprintf('$%.2f', shift->$amount_field() ) },
+                  sub { time2str('%b %d %Y', shift->_date ) },
+                  sub { my $o = shift->otaker;
+                        $o = 'auto billing'          if $o eq 'fs_daily';
+                        $o = 'customer self-service' if $o eq 'fs_selfservice';
+                        $o;
+                      },
+                  \&FS::UI::Web::cust_fields,
+                ],
+                #'align' => 'lrrrll',
+                'align' => 'rrrc'.FS::UI::Web::cust_aligns(),
+                'links' => [
+                  $link,
+                  $link,
+                  $link,
+                  '',
+                  ( map { $_ ne 'Cust. Status' ? $cust_link : '' }
+                        FS::UI::Web::cust_header()
+                  ),
+                ],
+                'color' => [ 
+                             '',
+                             '',
+                             '',
+                             '',
+                             FS::UI::Web::cust_colors(),
+                           ],
+                'style' => [ 
+                             '',
+                             '',
+                             '',
+                             '',
+                             FS::UI::Web::cust_styles(),
+                           ],
+          )
+%>
+<%init>
+
+my %opt = @_;
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $thing = $opt{'thing'};
+my $amount_field = $opt{'amount_field'};
+my $name_singular = $opt{'name_singular'};
+
+my $title = "\u$name_singular Search Results";
+my( $count_query, $sql_query );
+if ( $cgi->param('magic') ) {
+
+  my @search = ();
+  my $orderby;
+  if ( $cgi->param('magic') eq '_date' ) {
+
+
+    if ( $cgi->param('agentnum') && $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+      push @search, "agentnum = $1"; # $search{'agentnum'} = $1;
+      my $agent = qsearchs('agent', { 'agentnum' => $1 } );
+      die "unknown agentnum $1" unless $agent;
+      $title = $agent->agent. " $title";
+    }
+  
+    if ( $cgi->param('payby') ) {
+      $cgi->param('payby') =~
+        /^(CARD|CHEK|BILL|PREP|CASH|WEST|MCRD)(-(VisaMC|Amex|Discover|Maestro))?$/
+          or die "illegal payby ". $cgi->param('payby');
+      push @search, "cust_$thing.payby = '$1'";
+      if ( $3 ) {
+
+        my $cardtype = $3;
+
+        my $search;
+        if ( $cardtype eq 'VisaMC' ) {
+          #avoid posix regexes for portability
+          $search =
+            " ( (     substring(cust_$thing.payinfo from 1 for 1) = '4'     ".
+            "     AND substring(cust_$thing.payinfo from 1 for 4) != '4936' ".
+            "     AND substring(cust_$thing.payinfo from 1 for 6)           ".
+            "         NOT SIMILAR TO '49030[2-9]'                        ".
+            "     AND substring(cust_$thing.payinfo from 1 for 6)           ".
+            "         NOT SIMILAR TO '49033[5-9]'                        ".
+            "     AND substring(cust_$thing.payinfo from 1 for 6)           ".
+            "         NOT SIMILAR TO '49110[1-2]'                        ".
+            "     AND substring(cust_$thing.payinfo from 1 for 6)           ".
+            "         NOT SIMILAR TO '49117[4-9]'                        ".
+            "     AND substring(cust_$thing.payinfo from 1 for 6)           ".
+            "         NOT SIMILAR TO '49118[1-2]'                        ".
+            "   )".
+            "   OR substring(cust_$thing.payinfo from 1 for 2) = '51' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2) = '52' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2) = '53' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2) = '54' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2) = '54' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2) = '55' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2) = '36' ". #Diner's int'l processed as Visa/MC inside US
+            " ) ";
+        } elsif ( $cardtype eq 'Amex' ) {
+          $search =
+            " (    substring(cust_$thing.payinfo from 1 for 2 ) = '34' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2 ) = '37' ".
+            " ) ";
+        } elsif ( $cardtype eq 'Discover' ) {
+          $search =
+            " (    substring(cust_$thing.payinfo from 1 for 4 ) = '6011'  ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2 ) = '65'    ".
+            "   OR substring(cust_$thing.payinfo from 1 for 3 ) = '622'   ". #China Union Pay processed as Discover outside CN
+            " ) ";
+        } elsif ( $cardtype eq 'Maestro' ) { 
+          $search =
+            " (    substring(cust_$thing.payinfo from 1 for 2 ) = '63'     ".
+            "   OR substring(cust_$thing.payinfo from 1 for 2 ) = '67'     ".
+            "   OR substring(cust_$thing.payinfo from 1 for 6 ) = '564182' ".
+            "   OR substring(cust_$thing.payinfo from 1 for 4 ) = '4936'   ".
+            "   OR substring(cust_$thing.payinfo from 1 for 6 )            ".
+            "      SIMILAR TO '49030[2-9]'                             ".
+            "   OR substring(cust_$thing.payinfo from 1 for 6 )            ".
+            "      SIMILAR TO '49033[5-9]'                             ".
+            "   OR substring(cust_$thing.payinfo from 1 for 6 )            ".
+            "      SIMILAR TO '49110[1-2]'                             ".
+            "   OR substring(cust_$thing.payinfo from 1 for 6 )            ".
+            "      SIMILAR TO '49117[4-9]'                             ".
+            "   OR substring(cust_$thing.payinfo from 1 for 6 )            ".
+            "      SIMILAR TO '49118[1-2]'                             ".
+            " ) ";
+        } else {
+          die "unknown card type $cardtype";
+        }
+
+        my $masksearch = $search;
+        $masksearch =~ s/cust_$thing\.payinfo/cust_$thing.paymask/gi;
+
+        push @search,
+          "( $search OR ( cust_$thing.paymask IS NOT NULL AND $masksearch ) )";
+
+      }
+    }
+
+    if ( $cgi->param('payinfo') ) {
+      $cgi->param('payinfo') =~ /^\s*(\d+)\s*$/
+        or die "illegal payinfo ". $cgi->param('payinfo');
+      push @search, "cust_$thing.payinfo = '$1'";
+    }
+
+    my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+    push @search, "_date >= $beginning ",
+                  "_date <= $ending";
+
+    push @search, FS::UI::Web::parse_lt_gt($cgi, $amount_field );
+
+    $orderby = '_date';
+
+  } elsif ( $cgi->param('magic') eq 'paybatch' ) {
+
+    $cgi->param('paybatch') =~ /^([\w\/\:\-\.]+)$/
+      or die "illegal paybatch: ". $cgi->param('paybatch');
+
+    push @search, "paybatch = '$1'";
+
+    $orderby = "LOWER(company || ' ' || last || ' ' || first )";
+
+  } else {
+    die "unknown search magic: ". $cgi->param('magic');
+  }
+
+  #here is the agent virtualization
+  push @search, $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+  my $search = ' WHERE '. join(' AND ', @search);
+
+  $count_query = "SELECT COUNT(*), SUM($amount_field) ".
+                 "FROM cust_$thing LEFT JOIN cust_main USING ( custnum )".
+                 $search;
+
+  $sql_query = {
+    'table'     => "cust_$thing",
+    'select'    => join(', ',
+                     "cust_$thing.*",
+                     'cust_main.custnum as cust_main_custnum',
+                     FS::UI::Web::cust_sql_fields(),
+                   ),
+    'hashref'   => {},
+    'extra_sql' => "$search ORDER BY $orderby",
+    'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+  };
+
+} else {
+
+  #hmm... is this still used?
+
+  $cgi->param('payinfo') =~ /^\s*(\d+)\s*$/ or die "illegal payinfo";
+  my $payinfo = $1;
+
+  $cgi->param('payby') =~ /^(\w+)$/ or die "illegal payby";
+  my $payby = $1;
+
+  $count_query = "SELECT COUNT(*), SUM($amount_field) FROM cust_$thing".
+                 "  WHERE payinfo = '$payinfo' AND payby = '$payby'".
+                 "  AND ". $FS::CurrentUser::CurrentUser->agentnums_sql;
+
+  $sql_query = {
+    'table'     => "cust_$thing",
+    'hashref'   => { 'payinfo' => $payinfo,
+                     'payby'   => $payby    },
+    'extra_sql' => $FS::CurrentUser::CurrentUser->agentnums_sql.
+                   " ORDER BY _date",
+  };
+
+}
+
+my $link = '';
+if ( $thing eq 'pay' ) { #XXX no refund receipts yet
+  $link = [ "${p}view/cust_pay.html?paynum=", 'paynum' ]
+    if $FS::CurrentUser::CurrentUser->access_right('View invoices'); #XXX for now
+    #later# if $FS::CurrentUser::CurrentUser->access_right('View customer payments');
+}
+
+my $cust_link = sub {
+  my $cust_thing = shift;
+  $cust_thing->cust_main_custnum
+    ? [ "${p}view/cust_main.cgi?", 'custnum' ] 
+    : '';
+};
+
+</%init>