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;
 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;
 use Time::Local;
 use FS::UID qw( dbh );
 use FS::Report::Table;
@@ -9,8 +9,6 @@ use FS::CurrentUser;
 
 @ISA = qw( FS::Report::Table );
 
 
 @ISA = qw( FS::Report::Table );
 
-$expenses_kludge = 0;
-
 =head1 NAME
 
 FS::Report::Table::Monthly - Tables of report data, indexed monthly
 =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 ) = @_;
 
 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
 
 }
 
 #deferred revenue
 
-sub receipts { #cashflow
+sub cashflow {
   my( $self, $speriod, $eperiod, $agentnum ) = @_;
 
   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 {
 }
 
 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("
 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 ) = @_;
 #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);
 }
 
   $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 {
 #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 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;
 
 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 { 
 
 #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 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;
 
 use FS::cust_credit;
 use FS::cust_refund;
 
-@ISA = qw( FS::Record );
+@ISA = qw( FS::cust_main_Mixin FS::Record );
 
 =head1 NAME
 
 
 =head1 NAME
 
index 4981795..e075e3e 100644 (file)
@@ -11,7 +11,7 @@ use FS::cust_pay_refund;
 use FS::cust_main;
 use FS::payinfo_Mixin;
 
 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');
 
 
 @encrypted_fields = ('payinfo');
 
@@ -216,7 +216,7 @@ sub delete {
 
 =item replace OLD_RECORD
 
 
 =item replace OLD_RECORD
 
-Currently unimplemented (accounting reasons).
+Modifying a refund?  Well, don't say I didn't warn you.
 
 =cut
 
 
 =cut
 
index 6684c95..349d266 100644 (file)
@@ -124,7 +124,7 @@ sub can_payby {
   #return "Illegal payby" unless $hash{$payby};
   return 0 unless $hash{$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;
   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)
 
 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
 
 
 =head1 FIELDS
 
@@ -182,11 +180,6 @@ sub mask_payinfo {
   return $paymask;
 }
 
   return $paymask;
 }
 
-=cut
-
-sub _mask_payinfo {
-  my $self = shift;
-
 =item payinfo_check
 
 Checks payby and payinfo.
 =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
 
 =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
 
 
 =head1 SEE ALSO
 
index b996380..fbc31c0 100644 (file)
@@ -25,27 +25,40 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
 
 my $agentname = $agent ? $agent->agent.' ' : '';
 
 
 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 = (
 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 = (
 );
 
 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;
 
 );
 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 = (
   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;
 );
 $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;",
   '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?
 
 );
 # 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',
                  #'redirect'    => $link,
                  'header'      => [ 'Amount',
                                     'Date',
-                                    FS::UI::Web::cust_header(),
                                     'By',
                                     'By',
-                                    'Reason'
+                                    'Reason',
+                                    FS::UI::Web::cust_header(),
                                   ],
                  'fields'      => [
                    #'crednum',
                    sub { sprintf('$%.2f', shift->amount ) },
                    sub { time2str('%b %d %Y', shift->_date ) },
                                   ],
                  'fields'      => [
                    #'crednum',
                    sub { sprintf('$%.2f', shift->amount ) },
                    sub { time2str('%b %d %Y', shift->_date ) },
-                   \&FS::UI::Web::cust_fields,
                    'otaker',
                    'reason',
                    'otaker',
                    'reason',
+                   \&FS::UI::Web::cust_fields,
                  ],
                  #'align' => 'rrrllll',
                  ],
                  #'align' => 'rrrllll',
-                 'align' => 'rr'.FS::UI::Web::cust_aligns().'ll',
+                 'align' => 'rrll'.FS::UI::Web::cust_aligns(),
                  'links' => [
                    '',
                    '',
                  'links' => [
                    '',
                    '',
+                   '',
+                   '',
                    ( map { $_ ne 'Cust. Status' ? $clink : '' }
                          FS::UI::Web::cust_header()
                    ),
                    ( map { $_ ne 'Cust. Status' ? $clink : '' }
                          FS::UI::Web::cust_header()
                    ),
-                   '',
-                   '',
                  ],
                  'color' => [ 
                               '',
                               '',
                  ],
                  'color' => [ 
                               '',
                               '',
-                              FS::UI::Web::cust_colors(),
                               '',
                               '',
                               '',
                               '',
+                              FS::UI::Web::cust_colors(),
                             ],
                  'style' => [ 
                               '',
                               '',
                             ],
                  '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>