fix payment amount search, RT#34471
[freeside.git] / FS / FS / UI / Web.pm
index 91d2034..4461d99 100644 (file)
@@ -6,7 +6,7 @@ use Exporter;
 use Carp qw( confess );
 use HTML::Entities;
 use FS::Conf;
 use Carp qw( confess );
 use HTML::Entities;
 use FS::Conf;
-use FS::Misc::DateTime qw( parse_datetime );
+use FS::Misc::DateTime qw( parse_datetime day_end );
 use FS::Record qw(dbdef);
 use FS::cust_main;  # are sql_balance and sql_date_balance in the right module?
 
 use FS::Record qw(dbdef);
 use FS::cust_main;  # are sql_balance and sql_date_balance in the right module?
 
@@ -32,16 +32,16 @@ sub parse_beginning_ending {
   my $beginning = 0;
   if ( $cgi->param($prefix.'begin') =~ /^(\d+)$/ ) {
     $beginning = $1;
   my $beginning = 0;
   if ( $cgi->param($prefix.'begin') =~ /^(\d+)$/ ) {
     $beginning = $1;
-  } elsif ( $cgi->param($prefix.'beginning') =~ /^([ 0-9\-\/]{1,64})$/ ) {
+  } elsif ( $cgi->param($prefix.'beginning') =~ /^([ 0-9\-\/\:]{1,64})$/ ) {
     $beginning = parse_datetime($1) || 0;
   }
 
   my $ending = 4294967295; #2^32-1
   if ( $cgi->param($prefix.'end') =~ /^(\d+)$/ ) {
     $ending = $1 - 1;
     $beginning = parse_datetime($1) || 0;
   }
 
   my $ending = 4294967295; #2^32-1
   if ( $cgi->param($prefix.'end') =~ /^(\d+)$/ ) {
     $ending = $1 - 1;
-  } elsif ( $cgi->param($prefix.'ending') =~ /^([ 0-9\-\/]{1,64})$/ ) {
-    #probably need an option to turn off the + 86399
-    $ending = parse_datetime($1) + 86399;
+  } elsif ( $cgi->param($prefix.'ending') =~ /^([ 0-9\-\/\:]{1,64})$/ ) {
+    $ending = parse_datetime($1);
+    $ending = day_end($ending) unless $ending =~ /:/;
   }
 
   ( $beginning, $ending );
   }
 
   ( $beginning, $ending );
@@ -113,16 +113,16 @@ sub svc_url {
     if $DEBUG;
   if ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.cgi") ) {
     $url = "$svcdb.cgi?";
     if $DEBUG;
   if ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.cgi") ) {
     $url = "$svcdb.cgi?";
+  } elsif ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.html") ) {
+    $url = "$svcdb.html?";
   } else {
   } else {
-
     my $generic = $opt{action} eq 'search' ? 'cust_svc' : 'svc_Common';
 
     $url = "$generic.html?svcdb=$svcdb;";
     $url .= 'svcnum=' if $query =~ /^\d+(;|$)/ or $query eq '';
   }
 
     my $generic = $opt{action} eq 'search' ? 'cust_svc' : 'svc_Common';
 
     $url = "$generic.html?svcdb=$svcdb;";
     $url .= 'svcnum=' if $query =~ /^\d+(;|$)/ or $query eq '';
   }
 
-  import FS::CGI 'rooturl'; #WTF!  why is this necessary
-  my $return = rooturl(). "$opt{action}/$url$query";
+  my $return = FS::CGI::rooturl(). "$opt{action}/$url$query";
 
   $return = qq!<A HREF="$return">! if $opt{ahref};
 
 
   $return = qq!<A HREF="$return">! if $opt{ahref};
 
@@ -170,7 +170,8 @@ sub svc_export_links {
 }
 
 sub parse_lt_gt {
 }
 
 sub parse_lt_gt {
-  my($cgi, $field) = @_;
+  my($cgi, $field) = (shift, shift);
+  my $table = ( @_ && length($_[0]) ) ? shift.'.' : '';
 
   my @search = ();
 
 
   my @search = ();
 
@@ -188,7 +189,7 @@ sub parse_lt_gt {
 
       my $num = $1;
       $num =~ s/[\,\s]+//g;
 
       my $num = $1;
       $num =~ s/[\,\s]+//g;
-      my $search = "$field $op{$op} $num";
+      my $search = "$table$field $op{$op} $num";
       push @search, $search;
 
       warn "found ${field}_$op field; adding search element $search\n"
       push @search, $search;
 
       warn "found ${field}_$op field; adding search element $search\n"
@@ -225,43 +226,45 @@ sub cust_header {
 
   my %header2method = (
     'Customer'                 => 'name',
 
   my %header2method = (
     'Customer'                 => 'name',
-    'Cust. Status'             => 'ucfirst_cust_status',
+    'Cust. Status'             => 'cust_status_label',
     'Cust#'                    => 'custnum',
     'Name'                     => 'contact',
     'Company'                  => 'company',
     'Cust#'                    => 'custnum',
     'Name'                     => 'contact',
     'Company'                  => 'company',
+
+    # obsolete but might still be referenced in configuration
     '(bill) Customer'          => 'name',
     '(service) Customer'       => 'ship_name',
     '(bill) Name'              => 'contact',
     '(service) Name'           => 'ship_contact',
     '(bill) Company'           => 'company',
     '(service) Company'        => 'ship_company',
     '(bill) Customer'          => 'name',
     '(service) Customer'       => 'ship_name',
     '(bill) Name'              => 'contact',
     '(service) Name'           => 'ship_contact',
     '(bill) Company'           => 'company',
     '(service) Company'        => 'ship_company',
+    '(bill) Day phone'         => 'daytime',
+    '(bill) Night phone'       => 'night',
+    '(bill) Fax number'        => 'fax',
+    'Customer'                 => 'name',
     'Address 1'                => 'bill_address1',
     'Address 2'                => 'bill_address2',
     'City'                     => 'bill_city',
     'State'                    => 'bill_state',
     'Zip'                      => 'bill_zip',
     'Address 1'                => 'bill_address1',
     'Address 2'                => 'bill_address2',
     'City'                     => 'bill_city',
     'State'                    => 'bill_state',
     'Zip'                      => 'bill_zip',
-    'Country'                  => 'country_full',
+    'Country'                  => 'bill_country_full',
     'Day phone'                => 'daytime', # XXX should use msgcat, but how?
     'Night phone'              => 'night',   # XXX should use msgcat, but how?
     'Day phone'                => 'daytime', # XXX should use msgcat, but how?
     'Night phone'              => 'night',   # XXX should use msgcat, but how?
+    'Mobile phone'             => 'mobile',  # XXX should use msgcat, but how?
     'Fax number'               => 'fax',
     '(bill) Address 1'         => 'bill_address1',
     '(bill) Address 2'         => 'bill_address2',
     '(bill) City'              => 'bill_city',
     '(bill) State'             => 'bill_state',
     '(bill) Zip'               => 'bill_zip',
     'Fax number'               => 'fax',
     '(bill) Address 1'         => 'bill_address1',
     '(bill) Address 2'         => 'bill_address2',
     '(bill) City'              => 'bill_city',
     '(bill) State'             => 'bill_state',
     '(bill) Zip'               => 'bill_zip',
-    '(bill) Country'           => 'country_full',
-    '(bill) Day phone'         => 'daytime', # XXX should use msgcat, but how?
-    '(bill) Night phone'       => 'night',   # XXX should use msgcat, but how?
-    '(bill) Fax number'        => 'fax',
+    '(bill) Country'           => 'bill_country_full',
     '(service) Address 1'      => 'ship_address1',
     '(service) Address 2'      => 'ship_address2',
     '(service) City'           => 'ship_city',
     '(service) State'          => 'ship_state',
     '(service) Zip'            => 'ship_zip',
     '(service) Country'        => 'ship_country_full',
     '(service) Address 1'      => 'ship_address1',
     '(service) Address 2'      => 'ship_address2',
     '(service) City'           => 'ship_city',
     '(service) State'          => 'ship_state',
     '(service) Zip'            => 'ship_zip',
     '(service) Country'        => 'ship_country_full',
-    '(service) Day phone'      => 'ship_daytime', # XXX should use msgcat, how?
-    '(service) Night phone'    => 'ship_night',   # XXX should use msgcat, how?
-    '(service) Fax number'     => 'ship_fax',
     'Invoicing email(s)'       => 'invoicing_list_emailonly_scalar',
     'Payment Type'             => 'payby',
     'Current Balance'          => 'current_balance',
     'Invoicing email(s)'       => 'invoicing_list_emailonly_scalar',
     'Payment Type'             => 'payby',
     'Current Balance'          => 'current_balance',
@@ -322,6 +325,14 @@ sub cust_header {
   @cust_header;
 }
 
   @cust_header;
 }
 
+sub cust_sort_fields {
+  cust_header(@_) if( @_ or !@cust_fields );
+  #inefficientish, but tiny lists and only run once per page
+
+  map { $_ eq 'custnum' ? 'custnum' : '' } @cust_fields;
+
+}
+
 =item cust_sql_fields [ CUST_FIELDS_VALUE ]
 
 Returns a list of fields for the SELECT portion of an SQL query.
 =item cust_sql_fields [ CUST_FIELDS_VALUE ]
 
 Returns a list of fields for the SELECT portion of an SQL query.
@@ -337,7 +348,7 @@ sub cust_sql_fields {
   my @fields = qw( last first company );
 #  push @fields, map "ship_$_", @fields;
 
   my @fields = qw( last first company );
 #  push @fields, map "ship_$_", @fields;
 
-  cust_header(@_);
+  cust_header(@_) if( @_ or !@cust_fields );
   #inefficientish, but tiny lists and only run once per page
 
   my @location_fields;
   #inefficientish, but tiny lists and only run once per page
 
   my @location_fields;
@@ -348,8 +359,15 @@ sub cust_sql_fields {
       }
     }
   }
       }
     }
   }
-  
-  push @fields, 'payby' if grep { $_ eq 'payby'} @cust_fields;
+  foreach my $pre ('bill_','ship_') {
+    if ( grep { $_ eq $pre.'country_full' } @cust_fields ) {
+      push @location_fields, $pre.'locationnum';
+    }
+  }
+
+  foreach my $field (qw(daytime night mobile fax payby)) {
+    push @fields, $field if (grep { $_ eq $field } @cust_fields);
+  }
   push @fields, 'agent_custid';
 
   my @extra_fields = ();
   push @fields, 'agent_custid';
 
   my @extra_fields = ();
@@ -378,6 +396,9 @@ Otherwise, this function will assume the field is named "custnum".  If the
 argument isn't present at all, the join will just say "USING (custnum)", 
 which might work.
 
 argument isn't present at all, the join will just say "USING (custnum)", 
 which might work.
 
+As a special case, if TABLE is 'cust_main', only the joins to cust_location
+will be returned.
+
 LOCATION_TABLE is an optional table name to use for joining ship_location,
 in case your query also includes package information and you want the 
 "service address" columns to reflect package addresses.
 LOCATION_TABLE is an optional table name to use for joining ship_location,
 in case your query also includes package information and you want the 
 "service address" columns to reflect package addresses.
@@ -392,11 +413,12 @@ sub join_cust_main {
   ($location_table, $locationnum) = split(/\./, $location_table);
   $locationnum ||= 'locationnum';
 
   ($location_table, $locationnum) = split(/\./, $location_table);
   $locationnum ||= 'locationnum';
 
-  my $sql = ' LEFT JOIN cust_main ';
+  my $sql = '';
   if ( $cust_table ) {
   if ( $cust_table ) {
-    $sql .= "ON (cust_main.custnum = $cust_table.$custnum)";
+    $sql = " LEFT JOIN cust_main ON (cust_main.custnum = $cust_table.$custnum)"
+      unless $cust_table eq 'cust_main';
   } else {
   } else {
-    $sql .= "USING (custnum)";
+    $sql = " LEFT JOIN cust_main USING (custnum)";
   }
 
   if ( !@cust_fields or grep /^bill_/, @cust_fields ) {
   }
 
   if ( !@cust_fields or grep /^bill_/, @cust_fields ) {
@@ -466,25 +488,29 @@ element.
 
 sub cust_fields_subs {
   my $unlinked_warn = 0;
 
 sub cust_fields_subs {
   my $unlinked_warn = 0;
+
   return map { 
     my $f = $_;
   return map { 
     my $f = $_;
-    if( $unlinked_warn++ ) {
+    if ( $unlinked_warn++ ) {
+
       sub {
         my $record = shift;
       sub {
         my $record = shift;
-        if( $record->custnum ) {
-          $record->$f(@_);
-        }
-        else {
+        if ( $record->custnum ) {
+          encode_entities( $record->$f(@_) );
+        } else {
           '(unlinked)'
         };
           '(unlinked)'
         };
-      }
-    } 
-    else {
+      };
+
+    } else {
+
       sub {
         my $record = shift;
       sub {
         my $record = shift;
-        $record->$f(@_) if $record->custnum;
-      }
+        $record->custnum ? encode_entities( $record->$f(@_) ) : '';
+      };
+
     }
     }
+
   } @cust_fields;
 }
 
   } @cust_fields;
 }
 
@@ -549,6 +575,19 @@ sub cust_aligns {
   }
 }
 
   }
 }
 
+=item cust_links
+
+Returns an array of links to view/cust_main.cgi, for use with cust_fields.
+
+=cut
+
+sub cust_links {
+  my $link = [ FS::CGI::rooturl().'view/cust_main.cgi?', 'custnum' ];
+
+  return map { $_ eq 'cust_status_label' ? '' : $link }
+    @cust_fields;
+}
+
 =item is_mobile
 
 Utility function to determine if the client is a mobile browser.
 =item is_mobile
 
 Utility function to determine if the client is a mobile browser.
@@ -574,8 +613,8 @@ use vars qw($DEBUG);
 use Carp;
 use Storable qw(nfreeze);
 use MIME::Base64;
 use Carp;
 use Storable qw(nfreeze);
 use MIME::Base64;
-use JSON;
-use FS::UID qw(getotaker);
+use JSON::XS;
+use FS::CurrentUser;
 use FS::Record qw(qsearchs);
 use FS::queue;
 use FS::CGI qw(rooturl);
 use FS::Record qw(qsearchs);
 use FS::queue;
 use FS::CGI qw(rooturl);
@@ -649,7 +688,7 @@ sub start_job {
       push @{$param{$field}}, $value;
     }
   }
       push @{$param{$field}}, $value;
     }
   }
-  $param{CurrentUser} = getotaker();
+  $param{CurrentUser} = $FS::CurrentUser::CurrentUser->username;
   $param{RootURL} = rooturl($self->{cgi}->self_url);
   warn "FS::UI::Web::start_job\n".
        join('', map {
   $param{RootURL} = rooturl($self->{cgi}->self_url);
   warn "FS::UI::Web::start_job\n".
        join('', map {
@@ -669,10 +708,9 @@ sub start_job {
   #too slow to insert all the cgi params as individual args..,?
   #my $error = $queue->insert('_JOB', $cgi->Vars);
   
   #too slow to insert all the cgi params as individual args..,?
   #my $error = $queue->insert('_JOB', $cgi->Vars);
   
-  #warn 'froze string of size '. length(nfreeze(\%param)). " for job args\n"
-  #  if $DEBUG;
+  #rely on FS::queue smartness to freeze/encode the param hash
 
 
-  my $error = $job->insert( '_JOB', encode_base64(nfreeze(\%param)) );
+  my $error = $job->insert( '_JOB', \%param );
 
   if ( $error ) {
 
 
   if ( $error ) {
 
@@ -719,10 +757,7 @@ sub job_status {
     @return = ( 'error', $job ? $job->statustext : $jobnum );
   }
 
     @return = ( 'error', $job ? $job->statustext : $jobnum );
   }
 
-  #to_json(\@return);  #waiting on deb 5.0 for new JSON.pm?
-  #silence the warning though
-  my $to_json = JSON->can('to_json') || JSON->can('objToJson');
-  &$to_json(\@return);
+  encode_json \@return;
 
 }
 
 
 }