From 2d9689ef1601b66950413af88f319a6e4e2ccf0f Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Thu, 14 May 2015 15:34:17 -0400 Subject: Fix mis-spelling #35011 --- httemplate/view/cust_main/packages/status.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httemplate/view/cust_main/packages/status.html b/httemplate/view/cust_main/packages/status.html index 690f858ad..047abda0b 100644 --- a/httemplate/view/cust_main/packages/status.html +++ b/httemplate/view/cust_main/packages/status.html @@ -112,7 +112,7 @@ % } % if ( $curuser->access_right('Unsuspend customer package') ) { % if ( $cust_pkg->order_date eq $cust_pkg->get('susp') ) { #on hold - ( <% pkg_link('misc/unsusp_pkg', emt('Start bililng now'), $cust_pkg) %> ) + ( <% pkg_link('misc/unsusp_pkg', emt('Start billing now'), $cust_pkg) %> ) % } else { ( <% pkg_unsuspend_link($cust_pkg) %> ) ( <% pkg_resume_link($cust_pkg) %> ) -- cgit v1.2.1 From a15c09a76a0d94207f52526208feff90cb042364 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Sun, 17 May 2015 00:35:39 -0700 Subject: debug got left on accidentally --- FS/FS/ClientAPI.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FS/FS/ClientAPI.pm b/FS/FS/ClientAPI.pm index e4031b26e..1fea28c67 100644 --- a/FS/FS/ClientAPI.pm +++ b/FS/FS/ClientAPI.pm @@ -6,7 +6,7 @@ use vars qw( @EXPORT_OK %handler $domain $DEBUG $me ); @EXPORT_OK = qw( load_clientapi_modules ); -$DEBUG = 1; +$DEBUG = 0; $me = '[FS::ClientAPI]'; %handler = (); -- cgit v1.2.1 From 004a6337cff05d08db7abc400e1c09430f000ac0 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Sun, 17 May 2015 15:55:13 -0700 Subject: don't look up encryption config every record, RT#28526 --- FS/FS/Record.pm | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 88d452ae9..ec0ba142f 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -3,7 +3,7 @@ package FS::Record; use strict; use vars qw( $AUTOLOAD @ISA @EXPORT_OK $DEBUG %virtual_fields_cache - $conf $conf_encryption $money_char $lat_lower $lon_upper + $money_char $lat_lower $lon_upper $me $nowarn_identical $nowarn_classload $no_update_diff $no_history $qsearch_qualify_columns @@ -61,14 +61,20 @@ my $rsa_loaded; my $rsa_encrypt; my $rsa_decrypt; -$conf = ''; -$conf_encryption = ''; +our $conf = ''; +our $conf_encryption = ''; +our $conf_encryptionmodule = ''; +our $conf_encryptionpublickey = ''; +our $conf_encryptionprivatekey = ''; FS::UID->install_callback( sub { eval "use FS::Conf;"; die $@ if $@; $conf = FS::Conf->new; - $conf_encryption = $conf->exists('encryption'); + $conf_encryption = $conf->exists('encryption'); + $conf_encryptionmodule = $conf->exists('encryptionmodule'); + $conf_encryptionpublickey = $conf->exists('encryptionpublickey'); + $conf_encryptionprivatekey = $conf->exists('encryptionprivatekey'); $money_char = $conf->config('money_char') || '$'; my $nw_coords = $conf->exists('geocode-require_nw_coordinates'); $lat_lower = $nw_coords ? 1 : -90; @@ -1158,7 +1164,7 @@ sub insert { # Encrypt before the database if ( defined(eval '@FS::'. $table . '::encrypted_fields') && scalar( eval '@FS::'. $table . '::encrypted_fields') - && $conf->exists('encryption') + && $conf_encryption ) { foreach my $field (eval '@FS::'. $table . '::encrypted_fields') { next if $field eq 'payinfo' @@ -1399,7 +1405,7 @@ sub replace { # Encrypt for replace my $saved = {}; - if ( $conf->exists('encryption') + if ( $conf_encryption && defined(eval '@FS::'. $new->table . '::encrypted_fields') && scalar( eval '@FS::'. $new->table . '::encrypted_fields') ) { @@ -2160,7 +2166,7 @@ sub _h_statement { ; # If we're encrypting then don't store the payinfo in the history - if ( $conf && $conf->exists('encryption') && $self->table ne 'banned_pay' ) { + if ( $conf_encryption && $self->table ne 'banned_pay' ) { @fields = grep { $_ ne 'payinfo' } @fields; } @@ -3057,7 +3063,7 @@ sub encrypt { my ($self, $value) = @_; my $encrypted = $value; - if ($conf->exists('encryption')) { + if ($conf_encryption) { if ($self->is_encrypted($value)) { # Return the original value if it isn't plaintext. $encrypted = $value; @@ -3105,7 +3111,7 @@ You should generally not have to worry about calling this, as the system handles sub decrypt { my ($self,$value) = @_; my $decrypted = $value; # Will return the original value if it isn't encrypted or can't be decrypted. - if ($conf->exists('encryption') && $self->is_encrypted($value)) { + if ($conf_encryption && $self->is_encrypted($value)) { $self->loadRSA; if (ref($rsa_decrypt) =~ /::RSA/) { my $encrypted = unpack ("u*", $value); @@ -3121,8 +3127,8 @@ sub loadRSA { #Initialize the Module $rsa_module = 'Crypt::OpenSSL::RSA'; # The Default - if ($conf->exists('encryptionmodule') && $conf->config('encryptionmodule') ne '') { - $rsa_module = $conf->config('encryptionmodule'); + if ($conf_encryptionmodule && $conf_encryptionmodule ne '') { + $rsa_module = $conf_encryptionmodule; } if (!$rsa_loaded) { @@ -3130,14 +3136,14 @@ sub loadRSA { $rsa_loaded++; } # Initialize Encryption - if ($conf->exists('encryptionpublickey') && $conf->config('encryptionpublickey') ne '') { - my $public_key = join("\n",$conf->config('encryptionpublickey')); + if ($conf_encryptionpublickey && $conf_encryptionpublickey ne '') { + my $public_key = join("\n",$conf_encryptionpublickey); $rsa_encrypt = $rsa_module->new_public_key($public_key); } # Intitalize Decryption - if ($conf->exists('encryptionprivatekey') && $conf->config('encryptionprivatekey') ne '') { - my $private_key = join("\n",$conf->config('encryptionprivatekey')); + if ($conf_encryptionprivatekey && $conf_encryptionprivatekey ne '') { + my $private_key = join("\n",$conf_encryptionprivatekey); $rsa_decrypt = $rsa_module->new_private_key($private_key); } } -- cgit v1.2.1 From 529c23d58c97eca1d71df865988def671bfbd2cb Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Sun, 17 May 2015 16:21:05 -0700 Subject: don't look up encryption config every record, RT#28526 --- FS/FS/Record.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index ec0ba142f..b1e4a381c 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -72,9 +72,9 @@ FS::UID->install_callback( sub { die $@ if $@; $conf = FS::Conf->new; $conf_encryption = $conf->exists('encryption'); - $conf_encryptionmodule = $conf->exists('encryptionmodule'); - $conf_encryptionpublickey = $conf->exists('encryptionpublickey'); - $conf_encryptionprivatekey = $conf->exists('encryptionprivatekey'); + $conf_encryptionmodule = $conf->config('encryptionmodule'); + $conf_encryptionpublickey = $conf->config('encryptionpublickey'); + $conf_encryptionprivatekey = $conf->config('encryptionprivatekey'); $money_char = $conf->config('money_char') || '$'; my $nw_coords = $conf->exists('geocode-require_nw_coordinates'); $lat_lower = $nw_coords ? 1 : -90; -- cgit v1.2.1 From 3347ca190d669bc553888a2fbc88310b82f0eec8 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Sun, 17 May 2015 16:25:21 -0700 Subject: don't look up encryption config every record, RT#28526 --- FS/FS/Record.pm | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index b1e4a381c..72745fe13 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -73,8 +73,8 @@ FS::UID->install_callback( sub { $conf = FS::Conf->new; $conf_encryption = $conf->exists('encryption'); $conf_encryptionmodule = $conf->config('encryptionmodule'); - $conf_encryptionpublickey = $conf->config('encryptionpublickey'); - $conf_encryptionprivatekey = $conf->config('encryptionprivatekey'); + $conf_encryptionpublickey = join("\n",$conf->config('encryptionpublickey')); + $conf_encryptionprivatekey = join("\n",$conf->config('encryptionprivatekey')); $money_char = $conf->config('money_char') || '$'; my $nw_coords = $conf->exists('geocode-require_nw_coordinates'); $lat_lower = $nw_coords ? 1 : -90; @@ -3137,14 +3137,12 @@ sub loadRSA { } # Initialize Encryption if ($conf_encryptionpublickey && $conf_encryptionpublickey ne '') { - my $public_key = join("\n",$conf_encryptionpublickey); - $rsa_encrypt = $rsa_module->new_public_key($public_key); + $rsa_encrypt = $rsa_module->new_public_key($conf_encryptionpublickey); } # Intitalize Decryption if ($conf_encryptionprivatekey && $conf_encryptionprivatekey ne '') { - my $private_key = join("\n",$conf_encryptionprivatekey); - $rsa_decrypt = $rsa_module->new_private_key($private_key); + $rsa_decrypt = $rsa_module->new_private_key($conf_encryptionprivatekey); } } -- cgit v1.2.1 From 4d7cb175253abd39124366d8f1dbf7ca83f71637 Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Sun, 17 May 2015 16:29:14 -0700 Subject: add rate name to CDR report, RT#34985 --- httemplate/search/cdr.html | 22 ++++++++++++++++------ httemplate/search/report_cdr.html | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/httemplate/search/cdr.html b/httemplate/search/cdr.html index 778799b1f..e57913e5f 100644 --- a/httemplate/search/cdr.html +++ b/httemplate/search/cdr.html @@ -289,11 +289,11 @@ my $qsearch = join(' AND ', @qsearch); $qsearch = ( scalar(keys %$hashref) ? ' AND ' : ' WHERE ' ) . $qsearch if $qsearch; -my $query = { 'table' => 'cdr', - 'hashref' => $hashref, - 'extra_sql' => $qsearch, - 'order_by' => 'ORDER BY calldate', - }; +my $query = { 'table' => 'cdr', + 'hashref' => $hashref, + 'extra_sql' => $qsearch, + 'order_by' => 'ORDER BY calldate', + }; if ( $cgi->param('longest') =~ /^(\d+)$/ && $cgi->param('longest') > 0 ) { $cgi->param('maxrecords',$1); @@ -308,16 +308,26 @@ if ( $cgi->param('longest') =~ /^(\d+)$/ && $cgi->param('longest') > 0 ) { ### my %header = %{ FS::cdr->table_info->{'fields'} }; +$header{ratename} = 'Rate plan'; my @first = qw( acctid calldate clid charged_party src dst dcontext ); my %first = map { $_=>1 } @first; -my @fields = ( @first, grep !$first{$_}, fields('cdr') ); +my @fields = ( @first, + grep !$first{$_}, ( fields('cdr'), 'ratename' ) + ); if ( $cgi->param('show') ) { @fields = grep $cgi->param("show_$_"), @fields; } +if ( grep { $_ eq 'ratename' } @fields ) { + $query->{addl_from} .= ' + LEFT JOIN rate_detail ON (cdr.rated_ratedetailnum = rate_detail.ratedetailnum) + LEFT JOIN rate ON (rate_detail.ratenum = rate.ratenum) + '; +} + my @header = map { if ( exists($header{$_}) ) { $header{$_}; diff --git a/httemplate/search/report_cdr.html b/httemplate/search/report_cdr.html index 0eefd4ce7..ef5447838 100644 --- a/httemplate/search/report_cdr.html +++ b/httemplate/search/report_cdr.html @@ -188,7 +188,9 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('List rating data'); my @fields = fields('cdr'); +push @fields, 'ratename'; my $labels = FS::cdr->table_info->{'fields'}; +$labels->{ratename} = 'Rate plan'; my $conf = new FS::Conf; my $default_phone_countrycode = -- cgit v1.2.1 From 95f871fc98eff48bb4af211d705f788a06d3bf4c Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Sun, 17 May 2015 17:38:29 -0700 Subject: payment application detail report, RT#34871 --- FS/FS/cust_bill_pay_pkg.pm | 4 +- httemplate/elements/menu.html | 5 +- httemplate/search/cust_bill_pay_pkg.html | 572 ++++++++++++++++++++++++ httemplate/search/report_cust_bill_pay_pkg.html | 88 ++++ 4 files changed, 664 insertions(+), 5 deletions(-) create mode 100644 httemplate/search/cust_bill_pay_pkg.html create mode 100644 httemplate/search/report_cust_bill_pay_pkg.html diff --git a/FS/FS/cust_bill_pay_pkg.pm b/FS/FS/cust_bill_pay_pkg.pm index 5d154d0b7..24ca7973a 100644 --- a/FS/FS/cust_bill_pay_pkg.pm +++ b/FS/FS/cust_bill_pay_pkg.pm @@ -1,14 +1,12 @@ package FS::cust_bill_pay_pkg; +use base qw( FS::cust_main_Mixin FS::Record ); use strict; -use vars qw( @ISA ); use FS::Conf; use FS::Record qw( qsearch qsearchs ); use FS::cust_bill_pay; use FS::cust_bill_pkg; -@ISA = qw(FS::Record); - =head1 NAME FS::cust_bill_pay_pkg - Object methods for cust_bill_pay_pkg records diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 1bab4ef46..34bba7e8c 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -328,13 +328,14 @@ tie my %report_bill_event, 'Tie::IxHash', ; tie my %report_payments, 'Tie::IxHash', - 'Payments' => [ $fsurl.'search/report_cust_pay.html', 'Payment report (by type and/or date range)' ], + 'Payments' => [ $fsurl.'search/report_cust_pay.html', 'Payment report (by type and/or date range)' ], + 'Payment application detail' => [ $fsurl.'search/report_cust_bill_pay_pkg.html', 'Line item application detail' ], ; $report_payments{'Pending Payments'} = [ $fsurl.'search/cust_pay_pending.html?magic=_date;statusNOT=done', 'Pending real-time payments' ] if $curuser->access_right('View customer pending payments'); +$report_payments{'Unapplied Payments'} = [ $fsurl.'search/report_cust_pay.html?unapplied=1', 'Unapplied payment report (by type and/or date range)' ]; $report_payments{'Voided Payments'} = [ $fsurl.'search/report_cust_pay.html?void=1', 'Voided payment report (by type and/or date range)' ] if $curuser->access_right('View customer pending payments'); -$report_payments{'Unapplied Payments'} = [ $fsurl.'search/report_cust_pay.html?unapplied=1', 'Unapplied payment report (by type and/or date range)' ]; $report_payments{'Payment Batches'} = [ $fsurl.'search/pay_batch.html', 'Payment batches (by status and/or date range)' ] if $conf->exists('batch-enable') || $conf->config('batch-enable_payby'); $report_payments{'Unapplied Payment Aging'} = [ $fsurl.'search/report_unapplied_cust_pay.html', 'Unapplied payment aging report' ]; diff --git a/httemplate/search/cust_bill_pay_pkg.html b/httemplate/search/cust_bill_pay_pkg.html new file mode 100644 index 000000000..a6738f32c --- /dev/null +++ b/httemplate/search/cust_bill_pay_pkg.html @@ -0,0 +1,572 @@ +<& elements/search.html, + 'title' => 'Payment application detail', #to line item + 'name_singular' => 'payment application', + 'query' => $query, + 'count_query' => $count_query, + 'count_addl' => [ $money_char. '%.2f total', ], + 'header' => [ + #'#', + + 'Amount', + 'By', + '#', + + #payment + 'Date', + 'By', + + #application + 'Applied', + + # line item + 'Description', + 'Location', + @post_desc_header, + + #invoice + 'Invoice', + 'Date', + FS::UI::Web::cust_header(), + ], + 'fields' => [ + #'billpaypkgnum', + sub { sprintf($money_char.'%.2f', shift->paid ) }, + sub { $payby{shift->cust_bill_pay->cust_pay->payby} }, + sub { my $cust_pay = shift->cust_bill_pay->cust_pay; + $cust_pay->payby =~ /^(CARD|CHEK)$/ + ? $cust_pay->paymask : $cust_pay->payinfo; + }, + + sub { time2str('%b %d %Y', shift->get('cust_pay_date') ) }, + sub { shift->cust_bill_pay->cust_pay->otaker }, + + sub { sprintf($money_char.'%.2f', shift->amount ) }, + + sub { $_[0]->pkgnum > 0 + ? $_[0]->get('pkg') # possibly use override.pkg + : $_[0]->get('itemdesc') # but i think this correct + }, + $location_sub, + @post_desc, + 'invnum', + sub { time2str('%b %d %Y', shift->_date ) }, + \&FS::UI::Web::cust_fields, + ], + 'sort_fields' => [ + 'paid', + '', #payby + 'cust_pay_date', + '', #'otaker', + '', #amount + '', #line item description + '', #location + @post_desc_null, + 'invnum', + '_date', + #cust fields + ], + 'links' => [ + '', + '', + '', + '', + '', + '', + '', + '', + @post_desc_null, + $ilink, + $ilink, + ( map { $_ ne 'Cust. Status' ? $clink : '' } + FS::UI::Web::cust_header() + ), + ], + 'align' => 'rcrrlrlllrrcl'. + $post_desc_align. + 'rr'. + FS::UI::Web::cust_aligns(), + 'color' => [ + '', + '', + '', + '', + '', + '', + '', + '', + @post_desc_null, + '', + '', + FS::UI::Web::cust_colors(), + ], + 'style' => [ + '', + '', + '', + '', + '', + '', + '', + '', + @post_desc_null, + '', + '', + FS::UI::Web::cust_styles(), + ], + +&> +<%init> + +#(and now we're cust_bill_pay_pkg.html, false laziness w/cust_credit_bill_pkg) +#LOTS of false laziness below w/cust_bill_pkg.cgi +# and a little w/cust_credit.html + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +my $conf = new FS::Conf; + +my %payby = FS::payby->payby2shortname; + +#here is the agent virtualization +my $agentnums_sql = + $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' ); + +my @where = ( $agentnums_sql ); + +if ( $cgi->param('usernum') =~ /^(\d+)$/ ) { + push @where, "cust_pay.usernum = $1"; +} + +my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi); +push @where, "cust_bill._date >= $beginning", + "cust_bill._date <= $ending"; + +my($cr_begin, $cr_end) = FS::UI::Web::parse_beginning_ending($cgi, 'payment'); +push @where, "cust_pay._date >= $cr_begin", + "cust_pay._date <= $cr_end"; + +#payment amount? seems more what is expected than the applied amount +my @lt_gt = FS::UI::Web::parse_lt_gt($cgi, 'paid' ); +s/paid/cust_pay.paid/g foreach (@lt_gt); +push @where, @lt_gt; + +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { + push @where, "cust_main.agentnum = $1"; +} + +if ( $cgi->param('billpkgnum') =~ /^(\d+)$/ ) { + push @where, "billpkgnum = $1"; +} + +#classnum +# not specified: all classes +# 0: empty class +# N: classnum +my $use_override = $cgi->param('use_override'); +if ( $cgi->param('classnum') =~ /^(\d+)$/ ) { + my $comparison = ''; + if ( $1 == 0 ) { + $comparison = "IS NULL"; + } else { + $comparison = "= $1"; + } + + if ( $use_override ) { + push @where, "( + part_pkg.classnum $comparison AND pkgpart_override IS NULL OR + override.classnum $comparison AND pkgpart_override IS NOT NULL + )"; + } else { + push @where, "part_pkg.classnum $comparison"; + } +} + +if ( $cgi->param('taxclass') + && ! $cgi->param('istax') #no part_pkg.taxclass in this case + #(should we save a taxclass or a link to taxnum + # in cust_bill_pkg or something like + # cust_bill_pkg_tax_location?) + ) +{ + + #override taxclass when use_override is specified? probably + #if ( $use_override ) { + # + # push @where, + # ' ( '. join(' OR ', + # map { + # ' ( part_pkg.taxclass = '. dbh->quote($_). + # ' AND pkgpart_override IS NULL '. + # ' OR '. + # ' override.taxclass = '. dbh->quote($_). + # ' AND pkgpart_override IS NOT NULL '. + # ' ) ' + # } + # $cgi->param('taxclass') + # ). + # ' ) '; + # + #} else { + + push @where, + ' ( '. join(' OR ', + map ' part_pkg.taxclass = '.dbh->quote($_), + $cgi->param('taxclass') + ). + ' ) '; + + #} + +} + +my @loc_param = qw( district city county state country ); + +if ( $cgi->param('out') ) { + + my ( $loc_sql, @param ) = FS::cust_pkg->location_sql( 'ornull' => 1 ); + while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution + $loc_sql =~ s/\?/'cust_main_county.'.shift(@param)/e; + } + + $loc_sql =~ s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g + if $cgi->param('istax'); + + push @where, " + 0 = ( + SELECT COUNT(*) FROM cust_main_county + WHERE cust_main_county.tax > 0 + AND $loc_sql + ) + "; + + #not linked to by anything, but useful for debugging "out of taxable region" + if ( grep $cgi->param($_), @loc_param ) { + + my %ph = map { $_ => dbh->quote( scalar($cgi->param($_)) ) } @loc_param; + + my ( $loc_sql, @param ) = FS::cust_pkg->location_sql; + while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution + $loc_sql =~ s/\?/$ph{shift(@param)}/e; + } + + push @where, $loc_sql; + + } + +} elsif ( $cgi->param('country') ) { + + my @counties = $cgi->param('county'); + + if ( scalar(@counties) > 1 ) { + + #hacky, could be more efficient. care if it is ever used for more than the + # tax-report_groups filtering kludge + + my $locs_sql = + ' ( '. join(' OR ', map { + + my %ph = ( 'county' => dbh->quote($_), + map { $_ => dbh->quote( $cgi->param($_) ) } + qw( district city state country ) + ); + + my ( $loc_sql, @param ) = FS::cust_pkg->location_sql; + while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution + $loc_sql =~ s/\?/$ph{shift(@param)}/e; + } + + $loc_sql; + + } @counties + + ). ' ) '; + + push @where, $locs_sql; + + } else { + + my %ph = map { $_ => dbh->quote( scalar($cgi->param($_)) ) } @loc_param; + + my ( $loc_sql, @param ) = FS::cust_pkg->location_sql; + while ( $loc_sql =~ /\?/ ) { #easier to do our own substitution + $loc_sql =~ s/\?/$ph{shift(@param)}/e; + } + + push @where, $loc_sql; + + } + + my($title, $name); + if ( $cgi->param('istax') ) { + $title = 'Tax credits'; + $name = 'tax credits'; + if ( $cgi->param('taxname') ) { + push @where, 'itemdesc = '. dbh->quote( $cgi->param('taxname') ); + #} elsif ( $cgi->param('taxnameNULL') { + } else { + push @where, "( itemdesc IS NULL OR itemdesc = '' OR itemdesc = 'Tax' )"; + } + } elsif ( $cgi->param('nottax') ) { + $title = 'Payment applications to line items'; + $name = 'applications'; + #what can we usefully do with "taxname" ???? look up a class??? + } else { + $title = 'Payment applications to line items'; + $name = 'applications'; + #warn "neither nottax nor istax parameters specified"; + } + + if ( $cgi->param('taxclassNULL') + && ! $cgi->param('istax') #no taxclass in this case + ) + { + + my %hash = ( 'country' => scalar($cgi->param('country')) ); + foreach (qw( state county )) { + $hash{$_} = scalar($cgi->param($_)) if $cgi->param($_); + } + my $cust_main_county = qsearchs('cust_main_county', \%hash); + die "unknown base region for empty taxclass" unless $cust_main_county; + + my $same_sql = $cust_main_county->sql_taxclass_sameregion; + push @where, $same_sql if $same_sql; + + } + +} elsif ( scalar( grep( /locationtaxid/, $cgi->param ) ) ) { + + # this should really be shoved out to FS::cust_pkg->location_sql or something + # along with the code in report_newtax.cgi + + my %pn = ( + 'county' => 'tax_rate_location.county', + 'state' => 'tax_rate_location.state', + 'city' => 'tax_rate_location.city', + 'locationtaxid' => 'cust_bill_pkg_tax_rate_location.locationtaxid', + ); + + my %ph = map { ( $pn{$_} => dbh->quote( $cgi->param($_) || '' ) ) } + qw( city county state locationtaxid ); + + push @where, + join( ' AND ', map { "( $_ = $ph{$_} OR $ph{$_} = '' AND $_ IS NULL)" } + keys %ph + ); + +} + +if ( $cgi->param('itemdesc') ) { + if ( $cgi->param('itemdesc') eq 'Tax' ) { + push @where, "(itemdesc='Tax' OR itemdesc is null)"; + } else { + push @where, 'itemdesc='. dbh->quote($cgi->param('itemdesc')); + } +} + +if ( $cgi->param('report_group') =~ /^(=|!=) (.*)$/ && $cgi->param('istax') ) { + my ( $group_op, $group_value ) = ( $1, $2 ); + if ( $group_op eq '=' ) { + #push @where, 'itemdesc LIKE '. dbh->quote($group_value.'%'); + push @where, 'itemdesc = '. dbh->quote($group_value); + } elsif ( $group_op eq '!=' ) { + push @where, '( itemdesc != '. dbh->quote($group_value) .' OR itemdesc IS NULL )'; + } else { + die "guru meditation #00de: group_op $group_op\n"; + } + +} + +push @where, 'cust_bill_pkg.pkgnum != 0' if $cgi->param('nottax'); +push @where, 'cust_bill_pkg.pkgnum = 0' if $cgi->param('istax'); + +if ( $cgi->param('cust_tax') ) { + #false laziness -ish w/report_tax.cgi + my $cust_exempt; + if ( $cgi->param('taxname') ) { + my $q_taxname = dbh->quote($cgi->param('taxname')); + $cust_exempt = + "( tax = 'Y' + OR EXISTS ( SELECT 1 FROM cust_main_exemption + WHERE cust_main_exemption.custnum = cust_main.custnum + AND cust_main_exemption.taxname = $q_taxname ) + ) + "; + } else { + $cust_exempt = " tax = 'Y' "; + } + + push @where, $cust_exempt; +} + +my $count_query = "SELECT COUNT(DISTINCT billpaypkgnum), + SUM(cust_bill_pay_pkg.amount)"; + +my $join_cust = + ' JOIN cust_bill ON ( cust_bill_pkg.invnum = cust_bill.invnum )'. + FS::UI::Web::join_cust_main('cust_bill', 'cust_pkg'); + + +my $join_pkg; + +my $join_cust_bill_pkg = 'LEFT JOIN cust_bill_pkg USING ( billpkgnum )'; + +if ( $cgi->param('nottax') ) { + + $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum ) + LEFT JOIN part_pkg USING ( pkgpart ) + LEFT JOIN part_pkg AS override + ON pkgpart_override = override.pkgpart '; + $join_pkg .= ' LEFT JOIN cust_location USING ( locationnum ) ' + if $conf->exists('tax-pkg_address'); + +} elsif ( $cgi->param('istax') ) { + + #false laziness w/report_tax.cgi $taxfromwhere + if ( scalar( grep( /locationtaxid/, $cgi->param ) ) || + $cgi->param('iscredit') eq 'rate') { + + $join_pkg .= + ' LEFT JOIN cust_bill_pkg_tax_rate_location USING ( billpkgnum '; + if ( $cgi->param('iscredit') eq 'rate' ) { + $join_pkg .= ', billpkgtaxratelocationnum )'; + } elsif ( $conf->exists('tax-pkg_address') ) { + $join_pkg .= ', billpkgtaxlocationnum )'; + push @where, "billpkgtaxratelocationnum IS NULL"; + } else { + $join_pkg .= ' )'; + push @where, "billpkgtaxratelocationnum IS NULL"; + } + + $join_pkg .= ' LEFT JOIN tax_rate_location USING ( taxratelocationnum ) '; + + } elsif ( $conf->exists('tax-pkg_address') ) { + + $join_pkg .= ' LEFT JOIN cust_bill_pkg_tax_location USING ( billpkgnum ) + LEFT JOIN cust_location USING ( locationnum ) '; + + #quelle kludge, somewhat false laziness w/report_tax.cgi + s/cust_pkg\.locationnum/cust_bill_pkg_tax_location.locationnum/g for @where; + } + +} else { + + #warn "neither nottax nor istax parameters specified"; + #same as before? + $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum ) + LEFT JOIN part_pkg USING ( pkgpart ) '; + +} + +my $where = ' WHERE '. join(' AND ', @where); + +my $join_pay = ' LEFT JOIN cust_bill_pay USING ( billpaynum ) + LEFT JOIN cust_pay USING ( paynum ) '; + +$count_query .= " FROM cust_bill_pay_pkg + $join_cust_bill_pkg + $join_pkg + $join_pay + $join_cust + $where"; + +my @select = ( 'cust_bill_pay_pkg.*', + 'cust_bill_pkg.*', + 'cust_pay.paid', + 'cust_pay.otaker', + 'cust_pay._date AS cust_pay_date', + 'cust_bill._date', + ); +push @select, 'part_pkg.pkg' unless $cgi->param('istax'); +push @select, 'cust_main.custnum', + FS::UI::Web::cust_sql_fields(); + +my @post_desc_header = (); +my @post_desc = (); +my @post_desc_null = (); +my $post_desc_align = ''; +if ( $conf->exists('enable_taxclasses') && ! $cgi->param('istax') ) { + push @post_desc_header, 'Tax class'; + push @post_desc, 'taxclass'; + push @post_desc_null, ''; + $post_desc_align .= 'l'; + push @select, 'part_pkg.taxclass'; # or should this use override? +} + +warn "$join_cust_bill_pkg + $join_pkg + $join_pay + $join_cust"; + +my $query = { + 'table' => 'cust_bill_pay_pkg', + 'addl_from' => "$join_cust_bill_pkg + $join_pkg + $join_pay + $join_cust", + 'hashref' => {}, + 'select' => join(', ', @select ), + 'extra_sql' => $where, + 'order_by' => 'ORDER BY billpaypkgnum', #cust_bill. or cust_pay._date? +}; + +my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ]; +my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ]; + +my $conf = new FS::Conf; +my $money_char = $conf->config('money_char') || '$'; + +my $tax_pkg_address = $conf->exists('tax-pkg_address'); +my $tax_ship_address = $conf->exists('tax-ship_address'); + +my $location_sub = sub { + #my $cust_bill_pay_pkg = shift; + my $self = shift; + my $tax_Xlocation = $self->cust_bill_pkg_tax_Xlocation; + if ( defined($tax_Xlocation) && $tax_Xlocation ) { + + if ( ref($tax_Xlocation) eq 'FS::cust_bill_pkg_tax_location' ) { + + if ( $tax_Xlocation->taxtype eq 'FS::cust_main_county' ) { + my $cust_main_county = $tax_Xlocation->cust_main_county; + if ( $cust_main_county ) { + $cust_main_county->label; + } else { + ''; #cust_main_county record is gone... history? yuck. + } + } else { + '(CCH tax_rate)'; #XXX FS::tax_rate.. vendor taxes not yet handled here + } + + } elsif ( ref($tax_Xlocation) eq 'FS::cust_bill_pkg_tax_rate_location' ) { + '(CCH)'; #XXX vendor taxes not yet handled here + } else { + 'unknown tax_Xlocation '. ref($tax_Xlocation); + } + + } else { + + my $cust_bill_pkg = $self->cust_bill_pkg; + if ( $cust_bill_pkg->pkgnum > 0 ) { + my $cust_pkg = $cust_bill_pkg->cust_pkg; + if ( $tax_pkg_address && (my $cust_location = $cust_pkg->cust_location) ){ + $cust_location->county_state_country; + } else { + my $cust_main = $cust_pkg->cust_main; + if ( $tax_ship_address && $cust_main->has_ship_address ) { + $cust_main->county_state_country('ship_'); + } else { + $cust_main->county_state_country; + } + } + + } else { + #tax? we shouldn't have wound up here then... + ''; #return customer ship or bill address? (depending on tax-ship_address) + } + + } + +}; + + diff --git a/httemplate/search/report_cust_bill_pay_pkg.html b/httemplate/search/report_cust_bill_pay_pkg.html new file mode 100644 index 000000000..22054e3e5 --- /dev/null +++ b/httemplate/search/report_cust_bill_pay_pkg.html @@ -0,0 +1,88 @@ +<& /elements/header.html, mt('Payment application report') &> + +
+ + + + +<& /elements/tr-select-user.html, + 'label' => emt('Employee: '), + 'access_user' => $access_user, +&> + +<& /elements/tr-select-agent.html, + curr_value => scalar( $cgi->param('agentnum') ), + #label => emt('Line items for agent: '), + disable_empty => 0, +&> + + + + + + +<& /elements/tr-input-beginning_ending.html, + 'prefix' => 'payment', +&> + +<& /elements/tr-input-lessthan_greaterthan.html, + label => emt('Amount'), + field => 'amount', +&> + + + +
+ +
+ + +
+ +<& /elements/footer.html &> +<%init> + +#Financial reports? +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +my $access_user = $FS::CurrentUser::CurrentUser->access_users_hashref('table' => 'cust_pay'); + +my $conf = new FS::Conf; + + + -- cgit v1.2.1 From 800328ef3d6ef9938a97fc8fd0af8c0d1f64bcad Mon Sep 17 00:00:00 2001 From: Ivan Kohler Date: Sun, 17 May 2015 17:39:26 -0700 Subject: payment application detail report, RT#34871 --- httemplate/search/report_cust_bill_pay_pkg.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httemplate/search/report_cust_bill_pay_pkg.html b/httemplate/search/report_cust_bill_pay_pkg.html index 22054e3e5..2347bab6d 100644 --- a/httemplate/search/report_cust_bill_pay_pkg.html +++ b/httemplate/search/report_cust_bill_pay_pkg.html @@ -38,7 +38,7 @@ <& /elements/tr-input-lessthan_greaterthan.html, label => emt('Amount'), - field => 'amount', + field => 'paid', &>