diff options
Diffstat (limited to 'httemplate')
44 files changed, 928 insertions, 942 deletions
diff --git a/httemplate/browse/discount.html b/httemplate/browse/discount.html index f26c161ca..d3cf873d0 100644 --- a/httemplate/browse/discount.html +++ b/httemplate/browse/discount.html @@ -8,8 +8,9 @@ 'count_query' => 'SELECT COUNT(*) FROM discount', 'disableable' => 1, 'disabled_statuspos' => 1, - 'header' => [ 'Name', 'Discount', ], + 'header' => [ 'Name', 'Class', 'Discount', ], 'fields' => [ 'name', + 'classname', 'description', ], 'links' => [ $link, diff --git a/httemplate/browse/discount_class.html b/httemplate/browse/discount_class.html new file mode 100644 index 000000000..7f09102ec --- /dev/null +++ b/httemplate/browse/discount_class.html @@ -0,0 +1,34 @@ +<% include( 'elements/browse.html', + 'title' => 'Discount classes', + 'html_init' => $html_init, + 'name' => 'discount classes', + 'disableable' => 1, + 'disabled_statuspos' => 1, + 'query' => { 'table' => 'discount_class', + 'hashref' => {}, + 'order_by' => 'ORDER BY classnum', + }, + 'count_query' => $count_query, + 'header' => $header, + 'fields' => $fields, + 'links' => $links, + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +my $html_init = + 'Discount classes define reporing classifications for discounts.<BR><BR>'. + qq!<A HREF="${p}edit/discount_class.html"><I>Add a discount class</I></A><BR><BR>!; + +my $count_query = 'SELECT COUNT(*) FROM discount_class'; + +my $link = [ $p.'edit/discount_class.html?', 'classnum' ]; + +my $header = [ '#', 'Class' ]; +my $fields = [ 'classnum', 'classname' ]; +my $links = [ $link, $link ]; + +</%init> diff --git a/httemplate/browse/tower.html b/httemplate/browse/tower.html index 7f096a748..e2f9fd0bd 100644 --- a/httemplate/browse/tower.html +++ b/httemplate/browse/tower.html @@ -8,12 +8,13 @@ 'count_query' => 'SELECT COUNT(*) FROM tower', 'disableable' => 1, 'disabled_statuspos' => 1, - 'header' => [ 'Name', 'Sectors', 'Coordinates'], + 'header' => [ 'Name', 'Location', 'Sectors', ], 'fields' => [ $tower_sub, - $sector_sub, $coord_sub, + $sector_sub, ], 'links' => [ ], + 'cell_style' => [ $tagdesc_style ], ) %> <%init> @@ -21,6 +22,9 @@ die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); +#false laziness w/ browse/part_tag.html +my $tagdesc_style = sub { 'background-color:#'.shift->color }; + my $num_svc_links = sub { my ($query_string, $sectors) = @_; return if !$sectors; @@ -57,7 +61,12 @@ my $coord_sub = sub { [ [ - { 'data' => "Latitude: " . $tower->latitude . "<br>Longitude: " . $tower->longitude, }, + { 'data' => "Latitude: ". $tower->latitude. + "<br>Longitude: ". $tower->longitude. + "<br>Altitude: ". $tower->altitude. + "<br>Height: ". $tower->height. + "<br>Veg. height: ". $tower->veg_height, + }, { 'data' => $coords, 'link' => "Coordinates", }, ], ] @@ -70,7 +79,7 @@ my $tower_sub = sub { ); [ #rows [ - { 'data' => $tower->towername, }, + { 'data' => $tower->towername. ' ', }, { 'data' => ' (edit) ', size => '-1', 'link' => $p.'edit/tower.html?' . $tower->towernum }, ], @@ -89,7 +98,7 @@ my $sector_sub = sub { my $sectornum = $sector->sectornum; [ { - 'data' => $sector->sectorname, + 'data' => $sector->sectorname. ' ', 'link' => ( $sector->ip_addr ? 'http://'. $sector->ip_addr : '' ), }, diff --git a/httemplate/edit/access_user.html b/httemplate/edit/access_user.html index b087943c2..2d39f969c 100644 --- a/httemplate/edit/access_user.html +++ b/httemplate/edit/access_user.html @@ -7,17 +7,19 @@ 'last', 'first', { field=>'user_custnum', type=>'search-cust_main', }, + { field=>'report_salesnum', type=>'select-sales', empty_label=>'all', }, { field=>'disabled', type=>'checkbox', value=>'Y' }, ], 'labels' => { - 'usernum' => 'User number', - 'username' => 'Username', - '_password' => 'Password', - '_password2' => 'Re-enter Password', - 'last' => 'Last name', - 'first' => 'First name', - 'user_custnum' => 'Customer (optional)', - 'disabled' => 'Disable employee', + 'usernum' => 'User number', + 'username' => 'Username', + '_password' => 'Password', + '_password2' => 'Re-enter Password', + 'last' => 'Last name', + 'first' => 'First name', + 'user_custnum' => 'Customer (optional)', + 'report_salesnum' => 'Limit commission report to sales person', + 'disabled' => 'Disable employee', }, 'edit_callback' => \&edit_callback, 'field_callback'=> \&field_callback, @@ -68,8 +70,8 @@ my $check_user_custnum_search = <<END; END sub edit_callback { - my ($c, $o, $f, $opt) = @_; - $o->set('_password', ''); + my ($cgi, $access_user, $fields_listref, $opt_hashref) = @_; + $access_user->_password(''); } sub field_callback { diff --git a/httemplate/edit/discount.html b/httemplate/edit/discount.html index 9bcd1e724..c2853bd47 100644 --- a/httemplate/edit/discount.html +++ b/httemplate/edit/discount.html @@ -3,6 +3,7 @@ 'table' => 'discount', 'fields' => [ 'name', + { field => 'classnum', type => 'select-discount_class' }, { field => 'disabled', type => 'checkbox', value=>'Y', }, # a weird kind of false laziness # w/elements/tr-select-discount.html @@ -27,6 +28,7 @@ 'labels' => { 'discountnum' => 'Discount #', 'name' => 'Name ', + 'classnum' => 'Class', 'disabled' => 'Disabled ', '_type' => 'Type ', 'amount' => 'Amount ', diff --git a/httemplate/edit/discount_class.html b/httemplate/edit/discount_class.html new file mode 100644 index 000000000..2bf27d996 --- /dev/null +++ b/httemplate/edit/discount_class.html @@ -0,0 +1,10 @@ +<% include( 'elements/class_Common.html', + 'name_singular' => 'Discount class', + 'table' => 'discount_class', + 'nocat' => 1, + 'addl_labels' => { 'classnum' => 'Class', + 'classname' => 'Class', + 'disabled' => 'Disable', + }, + ) +%> diff --git a/httemplate/edit/elements/class_Common.html b/httemplate/edit/elements/class_Common.html index 0a0916ebc..723227f23 100644 --- a/httemplate/edit/elements/class_Common.html +++ b/httemplate/edit/elements/class_Common.html @@ -30,7 +30,13 @@ unless ( $opt{'nocat'} ) { my $fields = [ 'classname', (scalar(@category) - ? { field=>'categorynum', type=>'select-table', 'empty_label'=>'(none)', 'table'=>$category_table, 'name_col'=>'categoryname' } + ? { field => 'categorynum', + type => 'select-table', + table => $category_table, + hashref => { 'disabled' => '' }, + name_col => 'categoryname', + empty_label => '(none)', + } : { field=>'categorynum', type=>'hidden' } ), { field=>'disabled', type=>'checkbox', value=>'Y', }, diff --git a/httemplate/edit/elements/svc_Common.html b/httemplate/edit/elements/svc_Common.html index 321c68545..413150858 100644 --- a/httemplate/edit/elements/svc_Common.html +++ b/httemplate/edit/elements/svc_Common.html @@ -101,14 +101,17 @@ my $columndef = $part_svc->part_svc_column($f->{'field'}); my $flag = $columndef->columnflag; - if ( $flag eq 'F' ) { + + if ( $flag eq 'F' ) { #fixed $f->{'type'} = length($columndef->columnvalue) ? 'fixed' : 'hidden'; $f->{'value'} = $columndef->columnvalue; - } elsif ( $flag eq 'A' ) { + + } elsif ( $flag eq 'A' ) { #auto assign from inventory $f->{'type'} = 'hidden'; - } elsif ( $flag eq 'M' ) { + + } elsif ( $flag eq 'M' ) { #manually assign from inventory $f->{'type'} = 'select-inventory_item'; $f->{'empty_label'} = 'Select inventory item'; $f->{'extra_sql'} = 'WHERE ( svcnum IS NULL ' . @@ -116,11 +119,18 @@ ')'; $f->{'classnum'} = $columndef->columnvalue; $f->{'disable_empty'} = $object->svcnum ? 1 : 0; - } elsif ( $flag eq 'H' ) { + + } elsif ( $flag eq 'H' ) { #hardware $f->{'type'} = 'select-hardware_type'; $f->{'hashref'} = { 'classnum'=>$columndef->columnvalue }; + + } elsif ( $flag eq 'S' ) { #selectable choice + $f->{type} = 'select'; + $f->{options} = [ split( /\s*,\s*/, + $columndef->columnvalue) + ]; } if ( $f->{'type'} eq 'select-svc_pbx' diff --git a/httemplate/edit/process/discount_class.html b/httemplate/edit/process/discount_class.html new file mode 100644 index 000000000..e7249464b --- /dev/null +++ b/httemplate/edit/process/discount_class.html @@ -0,0 +1,11 @@ +<% include( 'elements/process.html', + 'table' => 'discount_class', + 'viewall_dir' => 'browse', + ) +%> +<%init> + +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('Configuration'); + +</%init> diff --git a/httemplate/edit/process/tower.html b/httemplate/edit/process/tower.html index 7353784e5..bbfc1a670 100644 --- a/httemplate/edit/process/tower.html +++ b/httemplate/edit/process/tower.html @@ -2,6 +2,6 @@ table => 'tower', viewall_dir => 'browse', process_o2m => { 'table' => 'tower_sector', - 'fields' => [qw( sectorname ip_addr )], + 'fields' => [qw( sectorname ip_addr height freq_mhz direction width range )], }, &> diff --git a/httemplate/edit/tower.html b/httemplate/edit/tower.html index 673a2713a..00c9adde1 100644 --- a/httemplate/edit/tower.html +++ b/httemplate/edit/tower.html @@ -4,24 +4,32 @@ viewall_dir => 'browse', fields => [ 'towername', { field=>'disabled', type=>'checkbox', value=>'Y', }, - { field => 'default_ip_addr', + { field=>'color', type=>'pickcolor' }, + { field => 'default_ip_addr', curr_value_callback => $default_ip_addr_callback }, + 'latitude', + 'longitude', + 'altitude', + 'height', + 'veg_height', { field => 'sectornum', type => 'tower_sector', o2m_table => 'tower_sector', m2_label => 'Sector', m2_error_callback => $m2_error_callback, }, - 'latitude', - 'longitude', ], - labels => { 'towernum' => 'Tower', - 'towername' => 'Name', - 'sectornum' => 'Sector', - 'disabled' => 'Disabled', + labels => { 'towernum' => 'Tower', + 'towername' => 'Name', + 'sectornum' => 'Sector', + 'disabled' => 'Disabled', 'default_ip_addr' => 'Tower IP address', - 'latitude' => 'Latitude', - 'longitude' => 'Longitude', + 'latitude' => 'Latitude', + 'longitude' => 'Longitude', + 'altitude' => 'Altitude', + 'height' => 'Height', + 'veg_height' => 'Vegetation height', + 'color' => 'Color', }, &> <%init> @@ -29,7 +37,7 @@ my $m2_error_callback = sub { # reconstruct the list my ($cgi, $object) = @_; - my @fields = qw(sectorname ip_addr); + my @fields = qw( sectorname ip_addr height freq_mhz direction width range ); map { my $k = $_; new FS::tower_sector { diff --git a/httemplate/elements/contact.html b/httemplate/elements/contact.html index 3d5177612..8abce058e 100644 --- a/httemplate/elements/contact.html +++ b/httemplate/elements/contact.html @@ -31,7 +31,7 @@ % 'phonetypenum' => $1, % }); % if ( $contact_phone ) { -% $value = $contact_phone->phonenum; +% $value = $contact_phone->phonenum_pretty; % $value .= 'x'.$contact_phone->extension % if $contact_phone->extension; % $value = '+'. $contact_phone->countrycode. " $value" diff --git a/httemplate/elements/input-text.html b/httemplate/elements/input-text.html index 827941501..18272ebe2 100644 --- a/httemplate/elements/input-text.html +++ b/httemplate/elements/input-text.html @@ -5,6 +5,7 @@ <% $size %> <% $maxlength %> <% $style %> + <% $opt{autocomplete} ? 'autocomplete="off"' : '' %> <% $opt{disabled} %> <% $onchange %> ><% $opt{'postfix'} %> diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index 8cb967518..b8575238d 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -564,6 +564,7 @@ if ( $curuser->access_right('Configuration') ) { #eo package grouping sub-menu $config_pkg{'Discounts'} = [ $fsurl.'browse/discount.html', '' ]; + $config_pkg{'Discount classes'} = [ $fsurl.'browse/discount_class.html', '' ]; $config_pkg{'Cancel/Suspend Reasons'} = [ \%config_pkg_reason, '' ]; } diff --git a/httemplate/elements/select-discount_class.html b/httemplate/elements/select-discount_class.html new file mode 100644 index 000000000..41a27c53a --- /dev/null +++ b/httemplate/elements/select-discount_class.html @@ -0,0 +1,18 @@ +<% include( '/elements/select-table.html', + 'table' => 'discount_class', + 'name_col' => 'classname', + 'value' => $classnum, + 'empty_label' => '(none)', + 'hashref' => { 'disabled' => '' }, + %opt, + ) +%> +<%init> + +my %opt = @_; +my $classnum = $opt{'curr_value'} || $opt{'value'}; + +$opt{'records'} = delete $opt{'discount_class'} + if $opt{'discount_class'}; + +</%init> diff --git a/httemplate/elements/standardize_locations.js b/httemplate/elements/standardize_locations.js index ad774d897..debd9e7c4 100644 --- a/httemplate/elements/standardize_locations.js +++ b/httemplate/elements/standardize_locations.js @@ -202,23 +202,32 @@ function post_standardization() { var cf = document.<% $formname %>; - if ( new String(cf.elements['<% $taxpre %>zip'].value).length < 10 ) + var prefix = '<% $taxpre %>'; + // fix edge case with cust_main + if ( cf.elements['same'] + && cf.elements['same'].checked + && prefix == 'ship_' ) { + + prefix = 'bill_'; + } + + if ( new String(cf.elements[prefix + 'zip'].value).length < 10 ) { - var country_el = cf.elements['<% $taxpre %>country']; + var country_el = cf.elements[prefix + 'country']; var country = country_el.options[ country_el.selectedIndex ].value; - var geocode = cf.elements['<% $taxpre %>geocode'].value; + var geocode = cf.elements[prefix + 'geocode'].value; if ( country == 'CA' || country == 'US' ) { - var state_el = cf.elements['<% $taxpre %>state']; + var state_el = cf.elements[prefix + 'state']; var state = state_el.options[ state_el.selectedIndex ].value; var url = "<% $p %>/misc/choose_tax_location.html" + "?data_vendor=cch-zip" + - ";city=" + cf.elements['<% $taxpre %>city'].value + + ";city=" + cf.elements[prefix + 'city'].value + ";state=" + state + - ";zip=" + cf.elements['<% $taxpre %>zip'].value + + ";zip=" + cf.elements[prefix + 'zip'].value + ";country=" + country + ";geocode=" + geocode + ";formname=" + '<% $formname %>' + @@ -229,14 +238,14 @@ function post_standardization() { } else { - cf.elements['<% $taxpre %>geocode'].value = 'DEFAULT'; + cf.elements[prefix + 'geocode'].value = 'DEFAULT'; <% $post_geocode %>; } } else { - cf.elements['<% $taxpre %>geocode'].value = ''; + cf.elements[prefix + 'geocode'].value = ''; <% $post_geocode %>; } @@ -255,13 +264,19 @@ function update_geocode() { set_geocode = function (what) { var cf = document.<% $formname %>; + var prefix = '<% $taxpre %>'; + if ( cf.elements['same'] + && cf.elements['same'].checked + && prefix == 'ship_' ) { + prefix = 'bill_'; + } //alert(what.options[what.selectedIndex].value); var argsHash = eval('(' + what.options[what.selectedIndex].value + ')'); - cf.elements['<% $taxpre %>city'].value = argsHash['city']; - setselect(cf.elements['<% $taxpre %>state'], argsHash['state']); - cf.elements['<% $taxpre %>zip'].value = argsHash['zip']; - cf.elements['<% $taxpre %>geocode'].value = argsHash['geocode']; + cf.elements[prefix + 'city'].value = argsHash['city']; + setselect(cf.elements[prefix + 'state'], argsHash['state']); + cf.elements[prefix + 'zip'].value = argsHash['zip']; + cf.elements[prefix + 'geocode'].value = argsHash['geocode']; <% $post_geocode %>; } diff --git a/httemplate/elements/tower_sector.html b/httemplate/elements/tower_sector.html index a8bbbc5d1..406895ed9 100644 --- a/httemplate/elements/tower_sector.html +++ b/httemplate/elements/tower_sector.html @@ -53,6 +53,11 @@ my %size = ( 'title' => 12 ); tie my %label, 'Tie::IxHash', 'sectorname' => 'Name', 'ip_addr' => 'IP Address', + 'height' => 'Height', + 'freq_mhz' => 'Freq. (MHz)', + 'direction' => 'Direction', # or a button to set these to 0 for omni + 'width' => 'Width', # + 'range' => 'Range', ; my @fields = keys %label; diff --git a/httemplate/elements/tr-input-beginning_ending.html b/httemplate/elements/tr-input-beginning_ending.html index ffc903875..be6e03c6f 100644 --- a/httemplate/elements/tr-input-beginning_ending.html +++ b/httemplate/elements/tr-input-beginning_ending.html @@ -7,7 +7,7 @@ <TR> <TD ALIGN="right">From date: </TD> - <TD><INPUT TYPE="text" NAME="<% $opt{prefix} %>beginning" ID="<% $opt{prefix} %>beginning_text" VALUE="<% $from %>" SIZE=<%$size%> MAXLENGTH=<%$maxlength%>> <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $opt{prefix} %>beginning_button" STYLE="cursor: pointer" TITLE="Select date"><IMG SRC="<%$fsurl%>images/calendar-disabled.png" ID="<% $opt{prefix} %>beginning_disabled" STYLE="display:none"><BR><i>m/d/y<% $time_hint %></i></TD> + <TD><INPUT TYPE="text" NAME="<% $opt{prefix} %>beginning" ID="<% $opt{prefix} %>beginning_text" VALUE="<% $from ? time2str($date_format, $from) : '' %>" SIZE=<%$size%> MAXLENGTH=<%$maxlength%>> <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $opt{prefix} %>beginning_button" STYLE="cursor: pointer" TITLE="Select date"><IMG SRC="<%$fsurl%>images/calendar-disabled.png" ID="<% $opt{prefix} %>beginning_disabled" STYLE="display:none"><BR><i>m/d/y<% $time_hint %></i></TD> <SCRIPT TYPE="text/javascript"> Calendar.setup({ inputField: "<% $opt{prefix} %>beginning_text", @@ -26,7 +26,7 @@ % } <TD ALIGN="right">To date: </TD> - <TD><INPUT TYPE="text" NAME="<% $opt{prefix} %>ending" ID="<% $opt{prefix} %>ending_text" VALUE="<% $to %>" SIZE=<%$size%> MAXLENGTH=<%$maxlength%>> <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $opt{prefix} %>ending_button" STYLE="cursor: pointer" TITLE="Select date"><IMG SRC="<%$fsurl%>images/calendar-disabled.png" ID="<% $opt{prefix} %>ending_disabled" STYLE="display:none"><BR><i>m/d/y<% $time_hint %></i></TD> + <TD><INPUT TYPE="text" NAME="<% $opt{prefix} %>ending" ID="<% $opt{prefix} %>ending_text" VALUE="<% $to ? time2str($date_format, $to) : '' %>" SIZE=<%$size%> MAXLENGTH=<%$maxlength%>> <IMG SRC="<%$fsurl%>images/calendar.png" ID="<% $opt{prefix} %>ending_button" STYLE="cursor: pointer" TITLE="Select date"><IMG SRC="<%$fsurl%>images/calendar-disabled.png" ID="<% $opt{prefix} %>ending_disabled" STYLE="display:none"><BR><i>m/d/y<% $time_hint %></i></TD> <SCRIPT TYPE="text/javascript"> Calendar.setup({ inputField: "<% $opt{prefix} %>ending_text", diff --git a/httemplate/elements/tr-input-mask.html b/httemplate/elements/tr-input-mask.html index 33725b9a5..922f22f4b 100644 --- a/httemplate/elements/tr-input-mask.html +++ b/httemplate/elements/tr-input-mask.html @@ -5,6 +5,7 @@ % } <& /elements/tr-input-text.html, id => $id, @_ &> <script type="text/javascript"> +<&| /elements/onload.js &> MaskedInput({ elm: document.getElementById('<%$id%>'), format: '<% $opt{format} %>', @@ -12,7 +13,41 @@ MaskedInput({ <% $opt{typeon} ? "typeon: '$opt{typeon}'," : '' %> }); document.getElementById('<%$id%>').value = <% $value |js_string %>; +% if ( $clipboard_hack ) { +var t = document.getElementById('<% $id %>'); +var container = document.getElementById('<%$id%>_clipboard'); +var KeyHandlerDown = t.onkeydown +t.onkeydown = function(e) { + // intercept ctrl-c and ctrl-x + // and cmd-c and cmd-x on mac + // when text is selected + if ( ( e.ctrlKey || e.metaKey ) ) { + // do the dance + var separators = /[\\/:-]/g; + var s = t.value.substr(t.selectionStart, t.selectionEnd); + if ( s ) { + container.value = s.replace(separators, ''); + container.previous = t; + container.focus(); + container.select(); + return true; + } + } + return KeyHandlerDown.call(t, e); +}; +container.onkeyup = function(e) { + if ( container.previous ) { + setTimeout(function() { + container.previous.value = container.value; + container.previous.focus(); + }, 10); + } + return true; +} +% } # clipboard hack +</&> </script> +<textarea id="<%$id%>_clipboard" style="opacity:0"></textarea> <%shared> my $init = 0; </%shared> @@ -21,6 +56,8 @@ my %opt = @_; # must have a DOM id my $id = $opt{id} || sprintf('input%04d',int(rand(10000))); my $value = length($opt{curr_value}) ? $opt{curr_value} : $opt{value} || ''; + +my $clipboard_hack = $FS::CurrentUser::CurrentUser->option('enable_mask_clipboard_hack'); </%init> <%doc> Set up a text input field with input masking. diff --git a/httemplate/elements/tr-password.html b/httemplate/elements/tr-password.html index d19c4ed0e..8ad303c67 100644 --- a/httemplate/elements/tr-password.html +++ b/httemplate/elements/tr-password.html @@ -1,3 +1,4 @@ <& tr-input-text.html, @_, - 'type' => 'password', + 'type' => 'password', + 'autocomplete' => 1, &> diff --git a/httemplate/elements/tr-select-discount_class.html b/httemplate/elements/tr-select-discount_class.html new file mode 100644 index 000000000..5489fe6e8 --- /dev/null +++ b/httemplate/elements/tr-select-discount_class.html @@ -0,0 +1,27 @@ +% if ( scalar(@{ $opt{'discount_class'} }) == 0 ) { + + <INPUT TYPE="hidden" NAME="<% $opt{'element_name'} || $opt{'field'} || 'classnum' %>" VALUE=""> + +% } else { + + <TR> + <TD ALIGN="right"><% $opt{'label'} || 'Discount class' %></TD> + <TD> + <% include( '/elements/select-discount_class.html', + 'curr_value' => $classnum, + %opt + ) + %> + </TD> + </TR> + +% } + +<%init> + +my %opt = @_; +my $classnum = $opt{'curr_value'} || $opt{'value'}; + +$opt{'discount_class'} ||= [ qsearch( 'discount_class', { disabled=>'' } ) ]; + +</%init> diff --git a/httemplate/graph/report_cust_bill_pkg_discount.html b/httemplate/graph/report_cust_bill_pkg_discount.html index c599e71f1..6de84f80b 100644 --- a/httemplate/graph/report_cust_bill_pkg_discount.html +++ b/httemplate/graph/report_cust_bill_pkg_discount.html @@ -1,28 +1,35 @@ -<% include('/elements/header.html', 'Discount Report' ) %> +<& /elements/header.html', 'Discount Report' &> <FORM ACTION="cust_bill_pkg_discount.html" METHOD="GET"> <TABLE> -<% include('/elements/tr-select-from_to.html' ) %> +<!-- + <& /elements/tr-select-discount_class.html, + 'field' => 'discount_classnum', + 'pre_options' => [ '0' => 'all' ], + 'empty_label' => '(none)' + &> +--> -<% include('/elements/tr-select-agent.html', - 'label' => 'For agent: ', - 'disable_empty' => 0, - ) -%> + <& /elements/tr-select-from_to.html &> -%# anything about line items, discounts or packages really -%# otaker? -%# package class? -%# discount picker? (discount classes and categories? eek!) + <& /elements/tr-select-agent.html, + 'label' => 'For agent: ', + 'disable_empty' => 0, + &> + +% # anything about line items, discounts or packages really +% # otaker? +% # package class? +% # discount picker? (discount classes and categories? haha yup!) </TABLE> <BR><INPUT TYPE="submit" VALUE="Display"> </FORM> -<% include('/elements/footer.html') %> +<& /elements/footer.html &> <%init> die "access denied" diff --git a/httemplate/graph/report_money_time_daily.html b/httemplate/graph/report_money_time_daily.html index a436d0879..e80f5862c 100644 --- a/httemplate/graph/report_money_time_daily.html +++ b/httemplate/graph/report_money_time_daily.html @@ -4,12 +4,11 @@ <TABLE> -<% include( '/elements/tr-input-beginning_ending.html', +<& /elements/tr-input-beginning_ending.html, 'datesrequired' => 1, - 'from' => time2str('%m/%d/%Y',$from), - 'to' => time2str('%m/%d/%Y',time), - ) -%> + 'from' => $from, + 'to' => time, +&> <% include('/elements/tr-select-agent.html', 'label' => 'For agent: ', diff --git a/httemplate/loginout/login.html b/httemplate/loginout/login.html index d06d0a8fc..3c6e2ae9f 100644 --- a/httemplate/loginout/login.html +++ b/httemplate/loginout/login.html @@ -14,7 +14,7 @@ %# <FORM METHOD="POST" ACTION="<%$url_string%>loginout/login"> <FORM METHOD="POST" ACTION="/login"> - <INPUT TYPE="hidden" NAME="destination" VALUE="<% $r->prev->uri %>"> + <INPUT TYPE="hidden" NAME="destination" VALUE="<% $r->prev->unparsed_uri %>"> <TABLE CELLSPACING=0 CELLPADDING=4 BGCOLOR="#cccccc"> <TR> diff --git a/httemplate/misc/confirm-address_standardize.html b/httemplate/misc/confirm-address_standardize.html index 33d22195b..2eae011c4 100644 --- a/httemplate/misc/confirm-address_standardize.html +++ b/httemplate/misc/confirm-address_standardize.html @@ -19,52 +19,57 @@ Confirm address standardization % } % for my $pre (@prefixes) { % my $name = $pre eq 'bill_' ? 'billing' : 'service'; -% if ( $new{$pre.'addr_clean'} ) { +% if ( $new{$pre.'error'} ) { <TR> <TH>Entered <%$name%> address</TH> - <TH>Standardized <%$name%> address</TH> </TR> - <TR> % if ( $old{$pre.'company'} ) { <TR> <TD><% $old{$pre.'company'} %></TD> - <TD><% $new{$pre.'company'} %></TD> </TR> % } <TR> <TD><% $old{$pre.'address1'} %></TD> - <TD><% $new{$pre.'address1'} %></TD> + <TD ROWSPAN=3><FONT COLOR="#ff0000"><B><% $new{$pre.'error'} %></B></FONT></TD> </TR> <TR> <TD><% $old{$pre.'address2'} %></TD> - <TD><% $new{$pre.'address2'} %></TD> </TR> <TR> <TD><% $old{$pre.'city'} %>, <% $old{$pre.'state'} %> <% $old{$pre.'zip'} %></TD> - <TD><% $new{$pre.'city'} %>, <% $new{$pre.'state'} %> <% $new{$pre.'zip'} %></TD> </TR> - -% } # if addr_clean -% elsif ( $new{$pre.'error'} ) { +% } else { # not an error <TR> <TH>Entered <%$name%> address</TH> + <TH>Standardized <%$name%> address</TH> + </TR> +% if ( !$new{$pre.'addr_clean'} ) { + <TR> + <TD></TD> + <TH STYLE="font-size:smaller;color:#ff0000">(unverified)</TH> </TR> +% } + <TR> % if ( $old{$pre.'company'} ) { <TR> <TD><% $old{$pre.'company'} %></TD> + <TD><% $new{$pre.'company'} %></TD> </TR> % } <TR> <TD><% $old{$pre.'address1'} %></TD> - <TD ROWSPAN=3><FONT COLOR="#ff0000"><B><% $new{$pre.'error'} %></B></FONT></TD> + <TD><% $new{$pre.'address1'} %></TD> </TR> <TR> <TD><% $old{$pre.'address2'} %></TD> + <TD><% $new{$pre.'address2'} %></TD> </TR> <TR> <TD><% $old{$pre.'city'} %>, <% $old{$pre.'state'} %> <% $old{$pre.'zip'} %></TD> + <TD><% $new{$pre.'city'} %>, <% $new{$pre.'state'} %> <% $new{$pre.'zip'} %></TD> </TR> -% } #if error + +% } # if error % } # for $pre %# only do this part if address standardization provided a censustract @@ -88,7 +93,7 @@ Confirm address standardization </TR> % } #if censustract -% if ( $new{bill_error} or $new{ship_error} ) { +% if ( grep {$new{$_.'error'}} @prefixes ) { <TR> <TD ALIGN="center"> <BUTTON TYPE="button" STYLE="width:205px" onclick="confirm_manual_address();"> @@ -99,8 +104,7 @@ Confirm address standardization <IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission </BUTTON></TD> </TR> -% } -% else { +% } else { <TR> <TD ALIGN="center"> <BUTTON TYPE="button" STYLE="width:205px" onclick="confirm_manual_address();"> diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html index 962ee51b6..7edf8921c 100644 --- a/httemplate/pref/pref-process.html +++ b/httemplate/pref/pref-process.html @@ -54,6 +54,7 @@ unless ( $error ) { # if ($access_user) { spreadsheet_format mobile_menu enable_fuzzy_on_exact disable_html_editor disable_enter_submit_onetimecharge + enable_mask_clipboard_hack email_address snom-ip snom-username snom-password vonage-fromnumber vonage-username vonage-password diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html index d2b8835e7..ccfeecd77 100644 --- a/httemplate/pref/pref.html +++ b/httemplate/pref/pref.html @@ -137,6 +137,13 @@ Interface </TD> </TR> + <TR> + <TH ALIGN="right">Don't copy MAC address delimiters to clipboard</TH> + <TD ALIGN="left" COLSPAN=2> + <INPUT TYPE="checkbox" NAME="enable_mask_clipboard_hack" VALUE="1" <% $curuser->option('enable_mask_clipboard_hack') ? 'CHECKED' : '' %>> + </TD> + </TR> + </TABLE> <BR> diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi index 4c5e90f6a..d86641de3 100644 --- a/httemplate/search/cust_bill_pkg.cgi +++ b/httemplate/search/cust_bill_pkg.cgi @@ -144,8 +144,8 @@ Filtering parameters: - taxnum: Limit to items whose tax definition matches this taxnum. With "nottax" that means items that are subject to that tax; - with "istax" it's the tax charges themselves. Can be specified - more than once to include multiple taxes. + with "istax" it's the tax charges themselves. Can be a comma-separated + list to include multiple taxes. - country, state, county, city: Limit to items whose tax location matches these fields. If "nottax" it's the tax location of the package; @@ -283,24 +283,7 @@ if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { push @where, "cust_main.agentnum = $1"; } -# salesnum -if ( $cgi->param('salesnum') =~ /^(\d+)$/ ) { - - my $salesnum = $1; - - my $cmp_salesnum = $cgi->param('cust_main_sales') - ? ' COALESCE( cust_pkg.salesnum, cust_main.salesnum )' - : ' cust_pkg.salesnum '; - - push @where, "$cmp_salesnum = $salesnum"; - - #because currently we're called from sales_pkg_class.html for a specific - # class (or empty class) but not for all classes - #will have to do something to distinguish if someone wants the sales report - # (report_cust_bill_pkg.html) to have a sales person dropdown - $cgi->param('classnum', 0) unless $cgi->param('classnum'); -} - +# salesnum--see below # refnum if ( $cgi->param('refnum') =~ /^(\d+)$/ ) { push @where, "cust_main.refnum = $1"; @@ -350,7 +333,7 @@ if ( $cgi->param('nottax') ) { # 0: empty class # N: classnum if ( grep { $_ eq 'classnum' } $cgi->param ) { - my @classnums = grep /^\d+$/, $cgi->param('classnum'); + my @classnums = grep /^\d*$/, $cgi->param('classnum'); push @where, "COALESCE($part_pkg.classnum, 0) IN ( ". join(',', @classnums ). ' )' @@ -390,11 +373,8 @@ if ( $cgi->param('nottax') ) { # we don't handle exempt_monthly here if ( $cgi->param('taxname') ) { # specific taxname - push @tax_where, 'cust_main_county.taxname = '. + push @tax_where, "COALESCE(cust_main_county.taxname, 'Tax') = ". dbh->quote($cgi->param('taxname')); - } elsif ( $cgi->param('taxnameNULL') ) { - push @tax_where, 'cust_main_county.taxname IS NULL OR '. - 'cust_main_county.taxname = \'Tax\''; } # country:state:county:city:district (may be repeated) @@ -422,12 +402,8 @@ if ( $cgi->param('nottax') ) { } # specific taxnums - if ( $cgi->param('taxnum') ) { - my $taxnum_in = join(',', - grep /^\d+$/, $cgi->param('taxnum') - ); - push @tax_where, "cust_main_county.taxnum IN ($taxnum_in)" - if $taxnum_in; + if ( $cgi->param('taxnum') =~ /^([\d,]+)$/) { + push @tax_where, "cust_main_county.taxnum IN ($1)"; } # If we're showing exempt items, we need to find those with @@ -457,22 +433,16 @@ if ( $cgi->param('nottax') ) { USING (billpkgnum)"; } - if ( @tax_where or $cgi->param('taxable') or $cgi->param('out') ) { - # process tax restrictions - unshift @tax_where, - 'cust_main_county.tax > 0'; + # process tax restrictions + unshift @tax_where, + 'cust_bill_pkg_tax_location.taxable_billpkgnum = cust_bill_pkg.billpkgnum', + 'cust_main_county.tax > 0'; - my $tax_sub = "SELECT invnum, cust_bill_pkg_tax_location.pkgnum + my $tax_sub = "SELECT 1 FROM cust_bill_pkg_tax_location JOIN cust_bill_pkg AS tax_item USING (billpkgnum) JOIN cust_main_county USING (taxnum) - WHERE ". join(' AND ', @tax_where). - " GROUP BY invnum, cust_bill_pkg_tax_location.pkgnum"; - - $join_pkg .= " LEFT JOIN ($tax_sub) AS item_tax - ON (item_tax.invnum = cust_bill_pkg.invnum AND - item_tax.pkgnum = cust_bill_pkg.pkgnum)"; - } + WHERE ". join(' AND ', @tax_where); # now do something with that if ( @exempt_where ) { @@ -489,23 +459,17 @@ if ( $cgi->param('nottax') ) { my $taxable = 'cust_bill_pkg.setup + cust_bill_pkg.recur '. '- COALESCE(item_exempt.exempt_amount, 0)'; - push @where, 'item_tax.invnum IS NOT NULL'; push @select, "($taxable) AS taxable_amount"; + push @where, "EXISTS($tax_sub)"; push @peritem, 'taxable_amount'; push @peritem_desc, 'Taxable'; push @total, "SUM($taxable)"; push @total_desc, "$money_char%.2f taxable"; - } elsif ( $cgi->param('out') ) { - - push @where, 'item_tax.invnum IS NULL', - 'item_exempt.billpkgnum IS NULL'; - } elsif ( @tax_where ) { # union of taxable + all exempt_ cases - push @where, - '(item_tax.invnum IS NOT NULL OR item_exempt.billpkgnum IS NOT NULL)'; + push @where, "(EXISTS($tax_sub) OR item_exempt.billpkgnum IS NOT NULL)"; } @@ -572,6 +536,21 @@ if ( $cgi->param('nottax') ) { # don't double-count the components of consolidated taxes $total[0] = 'COUNT(DISTINCT cust_bill_pkg.billpkgnum)'; $total[1] = 'SUM(cust_bill_pkg_tax_location.amount)'; + + # package classnum + if ( grep { $_ eq 'classnum' } $cgi->param ) { + my @classnums = grep /^\d*$/, $cgi->param('classnum'); + $join_pkg .= ' + JOIN cust_pkg AS taxed_pkg + ON (cust_bill_pkg_tax_location.pkgnum = taxed_pkg.pkgnum) + JOIN part_pkg AS taxed_part_pkg + ON (taxed_pkg.pkgpart = taxed_part_pkg.pkgpart) + '; + push @where, "COALESCE(taxed_part_pkg.classnum, 0) IN ( ". + join(',', @classnums ). + ' )' + if @classnums; + } } # taxclass @@ -589,25 +568,8 @@ if ( $cgi->param('nottax') ) { } # specific taxnums - if ( $cgi->param('taxnum') ) { - my $taxnum_in = join(',', - grep /^\d+$/, $cgi->param('taxnum') - ); - push @where, "cust_main_county.taxnum IN ($taxnum_in)" - if $taxnum_in; - } - - # report group (itemdesc) - if ( $cgi->param('report_group') =~ /^(=|!=) (.*)$/ ) { - 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"; - } + if ( $cgi->param('taxnum') =~ /^([\d,]+)$/) { + push @where, "cust_main_county.taxnum IN ($1)"; } # itemdesc, for some reason @@ -704,6 +666,28 @@ if ( $cgi->param('credit') ) { push @select, 'cust_main.custnum', FS::UI::Web::cust_sql_fields(); +#salesnum +if ( $cgi->param('salesnum') =~ /^(\d+)$/ ) { + + my $salesnum = $1; + my $sales = FS::sales->by_key($salesnum) + or die "salesnum $salesnum not found"; + + my $subsearch = $sales->cust_bill_pkg_search('', '', + 'cust_main_sales' => ($cgi->param('cust_main_sales') ? 1 : 0), + 'paid' => ($cgi->param('paid') ? 1 : 0), + 'classnum' => scalar($cgi->param('classnum')) + ); + $join_pkg .= " JOIN sales_pkg_class ON ( COALESCE(sales_pkg_class.classnum, 0) = COALESCE( part_pkg.classnum, 0) )"; + + my $extra_sql = $subsearch->{extra_sql}; + $extra_sql =~ s/^WHERE//; + push @where, $extra_sql; + + $cgi->param('classnum', 0) unless $cgi->param('classnum'); +} + + my $where = join(' AND ', @where); $where &&= "WHERE $where"; diff --git a/httemplate/search/cust_bill_pkg_discount.html b/httemplate/search/cust_bill_pkg_discount.html index f598341a0..6da5787a0 100644 --- a/httemplate/search/cust_bill_pkg_discount.html +++ b/httemplate/search/cust_bill_pkg_discount.html @@ -7,6 +7,7 @@ 'header' => [ #'#', 'Discount', + 'Class', 'Amount', 'Months', 'Package', @@ -17,6 +18,7 @@ 'fields' => [ #'billpkgdiscountnum', sub { $_[0]->cust_pkg_discount->discount->description }, + sub { $_[0]->cust_pkg_discount->discount->classname }, sub { sprintf($money_char.'%.2f', shift->amount ) }, sub { my $m = shift->months; $m =~ /\./ ? sprintf('%.2f', $m) : $m; @@ -28,6 +30,7 @@ ], 'sort_fields' => [ '', + '', 'amount', 'months', 'pkg', @@ -40,6 +43,7 @@ '', '', '', + '', $ilink, $ilink, ( map { $_ ne 'Cust. Status' ? $clink : '' } @@ -47,7 +51,7 @@ ), ], #'align' => 'rlrrrc'.FS::UI::Web::cust_aligns(), - 'align' => 'lrrlrr'.FS::UI::Web::cust_aligns(), + 'align' => 'lcrrlrr'.FS::UI::Web::cust_aligns(), 'color' => [ #'', '', @@ -56,6 +60,7 @@ '', '', '', + '', FS::UI::Web::cust_colors(), ], 'style' => [ @@ -66,6 +71,7 @@ '', '', '', + '', FS::UI::Web::cust_styles(), ], @@ -98,7 +104,51 @@ if ( $cgi->param('usernum') =~ /^(\d+)$/ ) { push @where, "cust_pkg_discount.usernum = $1"; } -# #classnum +# (discount) classnum +my $join_discount = ''; +#false laziness w/cust_pkg_discount.html and cust_pkg.pm::search +if ( grep { $_ eq 'discount_classnum' } $cgi->param ) { + +# my @classnum = (); +# if ( ref($params->{'discount_classnum'}) ) { +# +# if ( ref($params->{'discount_classnum'}) eq 'HASH' ) { +# @classnum = grep $params->{'discount_classnum'}{$_}, keys %{ $params->{'discount_classnum'} }; +# } elsif ( ref($params->{'discount_classnum'}) eq 'ARRAY' ) { +# @classnum = @{ $params->{'discount_classnum'} }; +# } else { +# die 'unhandled discount_classnum ref '. $params->{'discount_classnum'}; +# } +# +# +# } elsif ( $params->{'discount_classnum'} =~ /^(\d*)$/ && $1 ne '0' ) { +# @classnum = ( $1 ); +# } +# +# if ( @classnum ) { + + if ( $cgi->param('discount_classnum') =~ /^(\d*)$/ && $1 ne '0' ) { + my @classnum = ( $1 ); + + $join_discount = 'LEFT JOIN discount USING (discountnum)'; + + my @c_where = (); + my @nums = grep $_, @classnum; + push @c_where, 'discount.classnum IN ('. join(',',@nums). ')' if @nums; + my $null = scalar( grep { $_ eq '' } @classnum ); + push @c_where, 'discount.classnum IS NULL' if $null; + + if ( scalar(@c_where) == 1 ) { + push @where, @c_where; + } elsif ( @c_where ) { + push @where, ' ( '. join(' OR ', @c_where). ' ) '; + } + + } + +} + +# #(package) classnum # # not specified: all classes # # 0: empty class # # N: classnum @@ -121,7 +171,7 @@ if ( $cgi->param('usernum') =~ /^(\d+)$/ ) { # } # } -my $count_query = "SELECT COUNT(*), SUM(amount)"; +my $count_query = "SELECT COUNT(*), SUM(cust_bill_pkg_discount.amount)"; my $join_cust_pkg_discount = 'LEFT JOIN cust_pkg_discount USING (pkgdiscountnum)'; @@ -137,11 +187,11 @@ my $join_pkg = #LEFT JOIN part_pkg AS override # ON pkgpart_override = override.pkgpart '; +my $join = "$join_cust_pkg_discount $join_discount $join_pkg $join_cust"; + my $where = ' WHERE '. join(' AND ', @where); -$count_query .= - " FROM cust_bill_pkg_discount $join_cust_pkg_discount $join_pkg $join_cust ". - $where; +$count_query .= " FROM cust_bill_pkg_discount $join $where"; my @select = ( 'cust_bill_pkg_discount.*', @@ -155,7 +205,7 @@ push @select, 'cust_main.custnum', my $query = { 'table' => 'cust_bill_pkg_discount', - 'addl_from' => "$join_cust_pkg_discount $join_pkg $join_cust", + 'addl_from' => $join, 'hashref' => {}, 'select' => join(', ', @select ), 'extra_sql' => $where, diff --git a/httemplate/search/cust_pkg.cgi b/httemplate/search/cust_pkg.cgi index 995779a46..54bfa00bf 100755 --- a/httemplate/search/cust_pkg.cgi +++ b/httemplate/search/cust_pkg.cgi @@ -175,6 +175,10 @@ for my $param (qw( censustract censustract2 )) { if grep { $_ eq $param } $cgi->param; } +#location flags (checkboxes) +my @loc = grep /^\w+$/, $cgi->param('loc'); +$search_hash{"location_$_"} = 1 foreach @loc; + my $report_option = $cgi->param('report_option'); $search_hash{report_option} = $report_option if $report_option; diff --git a/httemplate/search/cust_pkg_discount.html b/httemplate/search/cust_pkg_discount.html index 23af1802e..f0c7447a5 100644 --- a/httemplate/search/cust_pkg_discount.html +++ b/httemplate/search/cust_pkg_discount.html @@ -6,6 +6,7 @@ #'redirect' => $link, 'header' => [ 'Status', 'Discount', + 'Class', 'Months used', 'Employee', 'Package', @@ -16,6 +17,7 @@ 'fields' => [ sub { ucfirst( shift->status ) }, sub { shift->discount->description }, + sub { shift->discount->classname }, sub { my $m = shift->months_used; $m =~ /\./ ? sprintf('%.2f',$m) : $m; }, @@ -29,17 +31,19 @@ '', '', '', + '', ( map { $_ ne 'Cust. Status' ? $clink : ''} FS::UI::Web::cust_header() ), ], - 'align' => 'clrll'. FS::UI::Web::cust_aligns(), + 'align' => 'clcrll'. FS::UI::Web::cust_aligns(), 'color' => [ '', '', '', '', '', + '', FS::UI::Web::cust_colors(), ], 'style' => [ @@ -48,6 +52,7 @@ '', '', '', + '', FS::UI::Web::cust_styles(), ], @@ -78,6 +83,47 @@ if ( $cgi->param('status') eq 'active' ) { "; #XXX also end date } +#classnum +#false laziness w/cust_pkg.pm::search +if ( grep { $_ eq 'classnum' } $cgi->param ) { + +# my @classnum = (); +# if ( ref($params->{'classnum'}) ) { +# +# if ( ref($params->{'classnum'}) eq 'HASH' ) { +# @classnum = grep $params->{'classnum'}{$_}, keys %{ $params->{'classnum'} }; +# } elsif ( ref($params->{'classnum'}) eq 'ARRAY' ) { +# @classnum = @{ $params->{'classnum'} }; +# } else { +# die 'unhandled classnum ref '. $params->{'classnum'}; +# } +# +# +# } elsif ( $params->{'classnum'} =~ /^(\d*)$/ && $1 ne '0' ) { +# @classnum = ( $1 ); +# } +# +# if ( @classnum ) { + + if ( $cgi->param('classnum') =~ /^(\d*)$/ && $1 ne '0' ) { + my @classnum = ( $1 ); + + my @c_where = (); + my @nums = grep $_, @classnum; + push @c_where, 'discount.classnum IN ('. join(',',@nums). ')' if @nums; + my $null = scalar( grep { $_ eq '' } @classnum ); + push @c_where, 'discount.classnum IS NULL' if $null; + + if ( scalar(@c_where) == 1 ) { + push @where, @c_where; + } elsif ( @c_where ) { + push @where, ' ( '. join(' OR ', @c_where). ' ) '; + } + + } + +} + #usernum if ( $cgi->param('usernum') =~ /^(\d+)$/ ) { push @where, "cust_pkg_discount.usernum = $1"; diff --git a/httemplate/search/cust_tax_exempt_pkg.cgi b/httemplate/search/cust_tax_exempt_pkg.cgi index 40b9ed78f..382dbc476 100644 --- a/httemplate/search/cust_tax_exempt_pkg.cgi +++ b/httemplate/search/cust_tax_exempt_pkg.cgi @@ -101,7 +101,7 @@ my $join = " die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('View customer tax exemptions'); -my @where = ("exempt_monthly = 'Y'"); +my @where = ( "exempt_monthly = 'Y'" ); my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi); if ( $beginning || $ending ) { @@ -118,28 +118,7 @@ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) { push @where, "cust_main.custnum = $1"; } -if ( $cgi->param('out') ) { - # wtf? how would you ever get exemptions on a non-taxable package location? - - push @where, " - 0 = ( - SELECT COUNT(*) FROM cust_main_county AS county_out - WHERE ( county_out.county = cust_main.county - OR ( county_out.county IS NULL AND cust_main.county = '' ) - OR ( county_out.county = '' AND cust_main.county IS NULL) - OR ( county_out.county IS NULL AND cust_main.county IS NULL) - ) - AND ( county_out.state = cust_main.state - OR ( county_out.state IS NULL AND cust_main.state = '' ) - OR ( county_out.state = '' AND cust_main.state IS NULL ) - OR ( county_out.state IS NULL AND cust_main.state IS NULL ) - ) - AND county_out.country = cust_main.country - AND county_out.tax > 0 - ) - "; - -} elsif ( $cgi->param('country' ) ) { +if ( $cgi->param('country' ) ) { my $county = dbh->quote( $cgi->param('county') ); my $state = dbh->quote( $cgi->param('state') ); @@ -150,11 +129,19 @@ if ( $cgi->param('out') ) { push @where, 'taxclass = '. dbh->quote( $cgi->param('taxclass') ) if $cgi->param('taxclass'); -} elsif ( $cgi->param('taxnum') ) { +} + +if ( $cgi->param('taxnum') ) { - my $taxnum_in = join(',', grep /^\d+$/, $cgi->param('taxnum') ); - push @where, "taxnum IN ($taxnum_in)" if $taxnum_in; + my @taxnums = grep /^\d+$/, map { split(',', $_) } $cgi->param('taxnum'); + if ( $cgi->param('taxnum') =~ /^([\d,]+)$/) { + push @where, "cust_tax_exempt_pkg.taxnum IN ($1)"; + } + +} +if ( $cgi->param('classnum') =~ /^(\d+)$/ ) { + push @where, "COALESCE(part_pkg.classnum,0) = $1"; } my $where = scalar(@where) ? 'WHERE '.join(' AND ', @where) : ''; diff --git a/httemplate/search/report_cust_bill_pkg_discount.html b/httemplate/search/report_cust_bill_pkg_discount.html index f9ab901b5..77affd19d 100644 --- a/httemplate/search/report_cust_bill_pkg_discount.html +++ b/httemplate/search/report_cust_bill_pkg_discount.html @@ -1,30 +1,35 @@ -<% include('/elements/header.html', 'Discount report' ) %> +<& /elements/header.html, 'Discount report' &> <FORM ACTION="cust_bill_pkg_discount.html" METHOD="GET"> <TABLE> - <% include( '/elements/tr-select-user.html', - 'label' => 'Discounts by employee: ', - 'access_user' => \%access_user, - ) - %> + <& /elements/tr-select-discount_class.html, + 'field' => 'discount_classnum', + 'pre_options' => [ '0' => 'all' ], + 'empty_label' => '(none)' + &> - <% include( '/elements/tr-select-agent.html', - 'curr_value' => scalar( $cgi->param('agentnum') ), - 'label' => 'for agent: ', - 'disable_empty' => 0, - ) - %> + <& /elements/tr-select-user.html, + 'label' => 'Discounts by employee: ', + 'access_user' => \%access_user, + &> - <% include( '/elements/tr-input-beginning_ending.html' ) %> + <& /elements/tr-select-agent.html, + 'curr_value' => scalar( $cgi->param('agentnum') ), + 'label' => 'for agent: ', + 'disable_empty' => 0, + &> - <% include( '/elements/tr-input-lessthan_greaterthan.html', - 'label' => 'Amount', - 'field' => 'amount', - ) - %> + <& /elements/tr-input-beginning_ending.html &> + +<!-- doesn't actually work yet, needs support in cust_bill_pkg_discount.html + <& /elements/tr-input-lessthan_greaterthan.html, + 'label' => 'Amount', + 'field' => 'amount', + &> +--> </TABLE> @@ -33,7 +38,7 @@ </FORM> -<% include('/elements/footer.html') %> +<& /elements/footer.html &> <%init> die "access denied" diff --git a/httemplate/search/report_cust_main.html b/httemplate/search/report_cust_main.html index ebff7fadb..1ceb48e1e 100755 --- a/httemplate/search/report_cust_main.html +++ b/httemplate/search/report_cust_main.html @@ -172,20 +172,6 @@ &> <TR> - <TD ALIGN="right" VALIGN="center"><% mt('Without census tract') |h %></TD> - <TD><INPUT TYPE="checkbox" NAME="no_censustract"></TD> - </TR> - -% if ( $conf->exists('enable_taxproducts') ) { - - <TR> - <TD ALIGN="right" VALIGN="center"><% mt('With hardcoded tax location') |h %></TD> - <TD><INPUT TYPE="checkbox" NAME="with_geocode"></TD> - </TR> - -% } - - <TR> <TD ALIGN="right" VALIGN="center"><% mt('With email address(es)') |h %></TD> <TD><INPUT TYPE="checkbox" NAME="with_email"></TD> </TR> diff --git a/httemplate/search/report_cust_pkg.html b/httemplate/search/report_cust_pkg.html index f9aabfc7a..e75a0985b 100755 --- a/httemplate/search/report_cust_pkg.html +++ b/httemplate/search/report_cust_pkg.html @@ -8,11 +8,7 @@ <TABLE BGCOLOR="#cccccc" CELLSPACING=0> - <TR> - <TH CLASS="background" COLSPAN=2 ALIGN="left"> - <FONT SIZE="+1">Customer search options</FONT> - </TH> - </TR> + <& /elements/tr-title.html, value => mt('Customer search options') &> <& /elements/tr-select-agent.html, 'curr_value' => scalar( $cgi->param('agentnum') ), @@ -56,11 +52,7 @@ <TABLE BGCOLOR="#cccccc" CELLSPACING=0> - <TR> - <TH CLASS="background" COLSPAN=2 ALIGN="left"> - <FONT SIZE="+1">Package search options</FONT> - </TH> - </TR> + <& /elements/tr-title.html, value => mt('Package search options') &> <& /elements/tr-select-sales.html, 'label' => 'Package sales person', @@ -70,11 +62,10 @@ 'disable_empty' => 1, &> - <% include( '/elements/tr-select-cust_pkg-status.html', + <& /elements/tr-select-cust_pkg-status.html, 'label' => 'Package status', 'onchange' => 'status_changed(this);', - ) - %> + &> <SCRIPT TYPE="text/javascript"> @@ -120,23 +111,21 @@ </SCRIPT> - <% include( '/elements/tr-select-pkg_class.html', + <& /elements/tr-select-pkg_class.html, 'pre_options' => [ '0' => 'all' ], 'empty_label' => '(empty class)', - ) - %> + &> % if ( scalar( qsearch( 'part_pkg_report_option', { 'disabled' => '' } ) ) ) { - <% include( '/elements/tr-select-table.html', + <& /elements/tr-select-table.html, 'label' => 'Report classes', 'table' => 'part_pkg_report_option', 'name_col' => 'name', 'hashref' => { 'disabled' => '' }, 'element_name' => 'report_option', 'multiple' => 'multiple', - ) - %> + &> % } <TR> @@ -189,24 +178,38 @@ </SCRIPT> - <% include( '/elements/tr-checkbox.html', + <& /elements/tr-checkbox.html, 'label' => 'Custom packages', 'field' => 'custom', 'value' => 1, 'onchange' => 'custom_changed(this);', - ) - %> + &> - <% include( '/elements/tr-selectmultiple-part_pkg.html' ) %> + <& /elements/tr-selectmultiple-part_pkg.html &> - <TR> - <TH CLASS="background" COLSPAN=2> </TH> - </TR> + <& /elements/tr-title.html, value => mt('Location search options') &> - <TR> - <TH CLASS="background" COLSPAN=2 ALIGN="left"><FONT SIZE="+1">Display options</FONT></TH> - </TR> - <% include( '/elements/tr-select-cust-fields.html' ) %> +% my @location_options = qw(cust nocust census nocensus); +% if ( $conf->exists('enable_taxproducts') ) { +% push @location_options, 'geocode', 'nogeocode'; +% } + <& /elements/tr-checkbox-multiple.html, + 'label' => 'Where package location:', + 'field' => 'loc', + 'options' => \@location_options, + 'labels' => { 'cust' => "is the customer's default location", + 'nocust' => "is not the customer's default location", + 'census' => "has a census tract", + 'nocensus' => "does not have a census tract", + 'nogeocode'=> 'has an implicit tax location', + 'geocode' => 'has a hardcoded tax location', + }, + 'value' => { map { $_ => 1 } @location_options }, + &> + + <& /elements/tr-title.html, value => mt('Display options') &> + + <& /elements/tr-select-cust-fields.html &> </TABLE> @@ -276,4 +279,5 @@ my %checkbox = ( 'cancel' => 1, ); +my $conf = FS::Conf->new; </%once> diff --git a/httemplate/search/report_cust_pkg_discount.html b/httemplate/search/report_cust_pkg_discount.html index 31774c384..7f0e55fed 100644 --- a/httemplate/search/report_cust_pkg_discount.html +++ b/httemplate/search/report_cust_pkg_discount.html @@ -1,4 +1,4 @@ -<% include('/elements/header.html', 'Package discount report' ) %> +<& /elements/header.html, 'Package discount report' &> <FORM ACTION="cust_pkg_discount.html" METHOD="GET"> @@ -6,7 +6,7 @@ <TABLE> <TR> - <TD>Discount status</TD> + <TD ALIGN="right">Discount status</TD> <TD> <SELECT NAME="status"> <OPTION VALUE="active">Active @@ -16,18 +16,21 @@ </TD> </TR> - <% include( '/elements/tr-select-user.html', - 'label' => 'Discounts by employee: ', - 'access_user' => \%access_user, - ) - %> + <& /elements/tr-select-discount_class.html, + 'pre_options' => [ '0' => 'all' ], + 'empty_label' => '(none)' + &> - <% include( '/elements/tr-select-agent.html', - 'curr_value' => scalar( $cgi->param('agentnum') ), - 'label' => 'for agent: ', - 'disable_empty' => 0, - ) - %> + <& /elements/tr-select-user.html, + 'label' => 'Discounts by employee: ', + 'access_user' => \%access_user, + &> + + <& /elements/tr-select-agent.html, + 'curr_value' => scalar( $cgi->param('agentnum') ), + 'label' => 'for agent: ', + 'disable_empty' => 0, + &> </TABLE> @@ -36,7 +39,7 @@ </FORM> -<% include('/elements/footer.html') %> +<& /elements/footer.html &> <%init> die "access denied" diff --git a/httemplate/search/report_sales_commission.html b/httemplate/search/report_sales_commission.html index cc17e6bed..19af428d8 100644 --- a/httemplate/search/report_sales_commission.html +++ b/httemplate/search/report_sales_commission.html @@ -4,30 +4,47 @@ <TABLE BGCOLOR="#cccccc" CELLSPACING=0> -<& /elements/tr-select-agent.html, - 'onchange' => 'agent_changed(this)', -&> +% if ( $curuser->report_salesnum ) { + + <INPUT TYPE="hidden" NAME="agentnum" VALUE="<% $curuser->report_sales->agentnum %>"> + <INPUT TYPE="hidden" NAME="salesnum" VALUE="<% $curuser->report_salesnum %>"> + +% } else { + + <& /elements/tr-select-agent.html, + 'onchange' => 'agent_changed(this)', + &> -<SCRIPT TYPE="text/javascript"> + <SCRIPT TYPE="text/javascript"> - function agent_changed(what) { - salesnum_agentnum_changed(what); - } + function agent_changed(what) { + salesnum_agentnum_changed(what); + } - <&| /elements/onload.js &> - agent_changed(document.getElementById('agentnum')) - </&> + <&| /elements/onload.js &> + agent_changed(document.getElementById('agentnum')) + </&> -</SCRIPT> + </SCRIPT> -<& /elements/tr-select-sales.html &> + <& /elements/tr-select-sales.html, + 'empty_label' => 'all', + &> + +% } <& /elements/tr-checkbox.html, - 'label' => 'Customer sales person if there is no package sales person', - 'field' => 'cust_main_sales', - 'value' => 'Y', + 'label' => 'Customer sales person if there is no package sales person', + 'field' => 'cust_main_sales', + 'value' => 'Y', &> +<& /elements/tr-checkbox.html, + 'label' => 'Show paid sales only', + 'field' => 'paid', + 'value' => 'Y', +&> + <& /elements/tr-input-beginning_ending.html &> </TABLE> @@ -38,7 +55,8 @@ <% include('/elements/footer.html') %> <%init> -die "access denied" - unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); +my $curuser = $FS::CurrentUser::CurrentUser; + +die "access denied" unless $curuser->access_right('Financial reports'); </%init> diff --git a/httemplate/search/report_tax.cgi b/httemplate/search/report_tax.cgi index d71fcf90b..111f22d3d 100755 --- a/httemplate/search/report_tax.cgi +++ b/httemplate/search/report_tax.cgi @@ -1,283 +1,184 @@ -<% include("/elements/header.html", "$agentname Tax Report - ". - ( $beginning - ? time2str('%h %o %Y ', $beginning ) - : '' - ). - 'through '. - ( $ending == 4294967295 - ? 'now' - : time2str('%h %o %Y', $ending ) - ) - ) -%> +<& /elements/header.html, "$agentname Tax Report: ". + ( $beginning + ? time2str('%h %o %Y ', $beginning ) + : '' + ). + 'through '. + ( $ending == 4294967295 + ? 'now' + : time2str('%h %o %Y', $ending ) + ). ' - ' . $taxname +&> <TD ALIGN="right"> Download full results<BR> as <A HREF="<% $p.'search/report_tax-xls.cgi?'.$cgi->query_string%>">Excel spreadsheet</A> </TD> <STYLE type="text/css"> -td.sectionhead { +TD.sectionhead { background-color: #777777; color: #ffffff; font-weight: bold; text-align: left; } +.grid TH { background-color: #cccccc; padding: 0px 3px 2px } +.row0 TD { background-color: #eeeeee; padding: 0px 3px 2px; text-align: right} +.row1 TD { background-color: #ffffff; padding: 0px 3px 2px; text-align: right} +TD.rowhead { font-weight: bold; text-align: left } +.bigmath { font-size: large; font-weight: bold; font: sans-serif; text-align: center } </STYLE> -<% include('/elements/table-grid.html') %> +<& /elements/table-grid.html &> <TR> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" COLSPAN=9>Sales</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3>Rate</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3>Tax owed</TH> -% unless ( $cgi->param('show_taxclasses') ) { - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3>Tax invoiced</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3>Tax credited</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=3>Tax collected</TH> -% } + <TH ROWSPAN=3></TH> + <TH COLSPAN=5>Sales</TH> + <TH ROWSPAN=3></TH> + <TH ROWSPAN=3>Rate</TH> + <TH ROWSPAN=3></TH> + <TH ROWSPAN=3>Estimated tax</TH> + <TH ROWSPAN=3>Tax invoiced</TH> + <TH ROWSPAN=3></TH> + <TH ROWSPAN=3>Tax credited</TH> + <TH ROWSPAN=3></TH> + <TH ROWSPAN=3>Net tax due</TH> </TR> <TR> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2>Total</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=1>Non-taxable</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=1>Non-taxable</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=1>Non-taxable</TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2></TH> - <TH CLASS="grid" BGCOLOR="#cccccc" ROWSPAN=2>Taxable</TH> + <TH ROWSPAN=2>Total</TH> + <TH ROWSPAN=1>Non-taxable</TH> + <TH ROWSPAN=1>Non-taxable</TH> + <TH ROWSPAN=1>Non-taxable</TH> + <TH ROWSPAN=2>Taxable</TH> </TR> - <TR> - <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>(tax-exempt customer)</FONT></TH> - <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>(tax-exempt package)</FONT></TH> - <TH CLASS="grid" BGCOLOR="#cccccc"><FONT SIZE=-1>(monthly exemption)</FONT></TH> + <TR STYLE="font-size:small"> + <TH>(tax-exempt customer)</TH> + <TH>(tax-exempt package)</TH> + <TH>(monthly exemption)</TH> </TR> -% foreach my $class (@pkgclasses ) { -% next if @{ $class->{regions} } == 0; -% if ( $class->{classname} ) { - <TR> - <TD COLSPAN=19 CLASS="sectionhead"><% $class->{classname} %></TD> +% my $row = 0; +% my $classlink = ''; +% my $descend; +% $descend = sub { +% my ($data, $label) = @_; +% if ( ref $data eq 'ARRAY' ) { +% # then we've reached the bottom +% my (%taxnums, %values); +% foreach (@$data) { +% $taxnums{ $_->[0] } = $_->[1]; +% $values{ $_->[0] } = $_->[2]; +% } +% # finally, output + <TR CLASS="row<% $row %>"> +% # Row label + <TD CLASS="rowhead"><% $label |h %></TD> +% # Total Sales +% my $sales = $money_sprintf->( +% $values{taxable} + +% $values{exempt_cust} + +% $values{exempt_pkg} + +% $values{exempt_monthly} +% ); +% my %sales_taxnums; +% foreach my $x (qw(taxable exempt_cust exempt_pkg exempt_monthly)) { +% foreach (split(',', $taxnums{$x})) { +% $sales_taxnums{$_} = 1; +% } +% } +% my $sales_taxnums = join(',', keys %sales_taxnums); + <TD> + <A HREF="<% "$saleslink;$classlink;taxnum=$sales_taxnums" %>"> + <% $sales %> + </A> + </TD> +% # exemptions +% foreach(qw(cust pkg)) { + <TD> + <A HREF="<% "$saleslink;$classlink;exempt_$_=Y;taxnum=".$taxnums{"exempt_$_"} %>"> + <% $money_sprintf->($values{"exempt_$_"}) %> + </A> + </TD> +% } + <TD> + <A HREF="<% "$exemptlink;$classlink;taxnum=".$taxnums{"exempt_monthly"} %>"> + <% $money_sprintf->($values{"exempt_monthly"}) %> + </A> + </TD> +% # taxable + <TD> + <A HREF="<% "$saleslink;$classlink;taxable=1;taxnum=$taxnums{taxable}" %>"> + <% $money_sprintf->($values{taxable}) %> + </A> + </TD> +% # tax rate +% my $rate; +% foreach(split(',', $taxnums{tax})) { +% $rate ||= $taxrates{$_}; +% if ($rate != $taxrates{$_}) { +% $rate = 'variable'; +% last; +% } +% } +% $rate = sprintf('%.2f', $rate) . '%' if ($rate and $rate ne 'variable'); + <TD CLASS="bigmath"> × </TD> + <TD><% $rate %></TD> +% # estimated tax + <TD CLASS="bigmath"> = </TD> + <TD><% $rate eq 'variable' + ? '' + : $money_sprintf->( $values{taxable} * $rate / 100 ) %> + </TD> +% # invoiced tax + <TD> + <A HREF="<% "$taxlink;$classlink;taxnum=$taxnums{taxable}" %>"> + <% $money_sprintf->( $values{tax} ) %> + </A> + </TD> +% # credited tax + <TD CLASS="bigmath"> − </TD> + <TD> + <A HREF="<% "$creditlink;$classlink;taxnum=$taxnums{credited}" %>"> + <% $money_sprintf->( $values{credited} ) %> + </A> + </TD> +% # net tax due + <TD CLASS="bigmath"> = </TD> + <TD><% $money_sprintf->( $values{tax} - $values{credited} ) %></TD> </TR> -% } -% my $bgcolor1 = '#eeeeee'; -% my $bgcolor2 = '#ffffff'; -% my $bgcolor; - -% my @regions = @{ $class->{regions} }; -% foreach my $region ( @regions ) { -% -% my $link = ''; -% if ( $with_pkgclass and length($class->{classnum}) ) { -% $link = ';classnum='.$class->{classnum}; -% } # else we're not breaking down pkg class, or this is the grand total -% -% if ( $region->{'label'} eq $out ) { -% $link .= ';out=1'; -% } elsif ( $region->{'taxnums'} ) { -% # might be nicer to specify this as country:state:city -% $link .= ';'.join(';', map { "taxnum=$_" } @{ $region->{'taxnums'} }); -% } -% -% if ( $bgcolor eq $bgcolor1 ) { -% $bgcolor = $bgcolor2; -% } else { -% $bgcolor = $bgcolor1; -% } +% $row = $row ? 0 : 1; % -% my $hicolor = $bgcolor; -% unless ( $cgi->param('show_taxclasses') ) { -% my $diff = abs( sprintf( '%.2f', $region->{'owed'} ) -% - sprintf( '%.2f', $region->{'tax'} ) -% ); -% if ( $diff > 0.02 ) { -% $hicolor = $hicolor eq '#eeeeee' ? '#eeee99' : '#ffffcc'; +% } else { # we're not at the lowest classification +% my @keys = sort { $a <=> $b or $a cmp $b } keys(%$data); +% foreach my $key (@keys) { +% my $sublabel = join(', ', grep $_, $label, $key); +% &{ $descend }($data->{$key}, $sublabel); % } % } -% -% -% my $td = qq(TD CLASS="grid" BGCOLOR="$bgcolor"); -% my $tdh = qq(TD CLASS="grid" BGCOLOR="$hicolor"); -% my $bigmath = '<FONT FACE="sans-serif" SIZE="+1"><B>'; -% my $bme = '</B></FONT>'; - -% if ( $region->{'is_total'} ) { - <TR STYLE="font-style: italic"> - <TD STYLE="text-align: right; padding-right: 1ex; background-color:<%$bgcolor%>">Total</TD> -% } else { - <TR> - <<%$td%>><% $region->{'label'} %></TD> +% }; + +% my @pkgclasses = sort { $a <=> $b } keys %data; +% foreach my $pkgclass (@pkgclasses) { +% my $class = FS::pkg_class->by_key($pkgclass) || +% FS::pkg_class->new({ classname => 'Unclassified' }); + <TBODY> +% if ( $breakdown{pkgclass} ) { + <TR> + <TD COLSPAN=19 CLASS="sectionhead"><% $class->classname %></TD> + </TR> % } - <<%$td%> ALIGN="right"> - <A HREF="<% $baselink. $link %>;nottax=1" - ><% &$money_sprintf( $region->{'sales'} ) %></A> - </TD> -% if ( $region->{'label'} eq $out ) { - <<%$td%> COLSPAN=12></TD> -% } else { #not $out - <<%$td%>><FONT SIZE="+1"><B> - </B></FONT></TD> - <<%$td%> ALIGN="right"> - <A HREF="<% $baselink. $link %>;nottax=1;exempt_cust=Y" - ><% &$money_sprintf( $region->{'exempt_cust'} ) %></A> - </TD> - <<%$td%>><FONT SIZE="+1"><B> - </B></FONT></TD> - <<%$td%> ALIGN="right"> - <A HREF="<% $baselink. $link %>;nottax=1;exempt_pkg=Y" - ><% &$money_sprintf( $region->{'exempt_pkg'} ) %></A> - </TD> - <<%$td%>><FONT SIZE="+1"><B> - </B></FONT></TD> - <<%$td%> ALIGN="right"> - <A HREF="<% $exemptlink. $link %>" - ><% &$money_sprintf( $region->{'exempt_monthly'} ) %></A> - </TD> - <<%$td%>><FONT SIZE="+1"><B> = </B></FONT></TD> - <<%$td%> ALIGN="right"> - <A HREF="<% $baselink. $link %>;nottax=1;taxable=1" - ><% &$money_sprintf( $region->{'taxable'} ) %></A> - </TD> - <<%$td%>><% $region->{'label'} eq 'Total' ? '' : "$bigmath X $bme" %></TD> - <<%$td%> ALIGN="right"><% $region->{'rate'} %></TD> - <<%$td%>><% $region->{'label'} eq 'Total' ? '' : "$bigmath = $bme" %></TD> - <<%$tdh%> ALIGN="right"> - <% &$money_sprintf( $region->{'owed'} ) %> - </TD> -% } # if !$out -% unless ( $cgi->param('show_taxclasses') ) { -% my $invlink = $region->{'url_param_inv'} -% ? ';'. $region->{'url_param_inv'} -% : $link; - -% if ( $region->{'label'} eq $out ) { - <<%$td%> ALIGN="right"> - <A HREF="<% $baselink. $invlink %>;istax=1" - ><% &$money_sprintf_nonzero( $region->{'tax'} ) %></A> - </TD> - <<%$td%>></TD> - <<%$td%> ALIGN="right"> - <A HREF="<% $creditlink. $invlink %>;istax=1" - ><% &$money_sprintf_nonzero( $region->{'credit'} ) %></A> - </TD> - <<%$td%> COLSPAN=2></TD> -% } else { #not $out - <<%$tdh%> ALIGN="right"> - <A HREF="<% $baselink. $invlink %>;istax=1" - ><% &$money_sprintf( $region->{'tax'} ) %></A> - </TD> - <<%$tdh%>><FONT SIZE="+1"><B> - </B></FONT></TD> - <<%$tdh%> ALIGN="right"> - <A HREF="<% $creditlink. $invlink %>;istax=1" - ><% &$money_sprintf( $region->{'credit'} ) %></A> - </TD> - <<%$tdh%>><FONT SIZE="+1"><B> = </B></FONT></TD> - <<%$tdh%> ALIGN="right"> - <% &$money_sprintf( $region->{'tax'} - $region->{'credit'} ) %> - </TD> -% } -% } # show_taxclasses - - </TR> -% } # foreach $region - -%} # foreach $class - +% $row = 0; +% $classlink = "classnum=".($pkgclass || 0) if $breakdown{pkgclass}; +% &{ $descend }( $data{$pkgclass}, '' ); +% # and now totals + </TBODY> + <TBODY CLASS="total"> +% &{ $descend }( $total{$pkgclass}, 'Total' ); + </TBODY> +% } # foreach $pkgclass </TABLE> -% if ( $cgi->param('show_taxclasses') ) { - - <BR> - <% include('/elements/table-grid.html') %> - <TR> - <TH CLASS="grid" BGCOLOR="#cccccc"></TH> - <TH CLASS="grid" BGCOLOR="#cccccc">Tax invoiced</TH> - <TH CLASS="grid" BGCOLOR="#cccccc"></TH> - <TH CLASS="grid" BGCOLOR="#cccccc">Tax credited</TH> - <TH CLASS="grid" BGCOLOR="#cccccc"></TH> - <TH CLASS="grid" BGCOLOR="#cccccc">Tax collected</TH> - </TR> - -% #some false laziness w/above -% foreach my $class (@pkgclasses) { -% if ( $class->{classname} ) { - <TR> - <TD COLSPAN=6 CLASS="sectionhead"><% $class->{classname} %></TD> - </TR> -% } - -% my $bgcolor1 = '#eeeeee'; -% my $bgcolor2 = '#ffffff'; -% my $bgcolor; -% -% foreach my $region ( @{ $class->{base_regions} } ) { -% -% my $link = ''; -% if ( $with_pkgclass and length($class->{classnum}) ) { -% $link = ';classnum='.$class->{classnum}; -% } -% -% if ( $region->{'label'} eq $out ) { -% $link .= ';out=1'; -% } else { -% $link .= ';'. $region->{'url_param'} -% if $region->{'url_param'}; -% } -% -% if ( $bgcolor eq $bgcolor1 ) { -% $bgcolor = $bgcolor2; -% } else { -% $bgcolor = $bgcolor1; -% } -% my $td = qq(TD CLASS="grid" BGCOLOR="$bgcolor"); -% my $tdh = qq(TD CLASS="grid" BGCOLOR="$bgcolor"); -% -% #? -% my $invlink = $region->{'url_param_inv'} -% ? ';'. $region->{'url_param_inv'} -% : $link; - - <TR> - <<%$td%>><% $region->{'label'} %></TD> -% if ( $region->{'label'} eq $out ) { - <<%$td%> ALIGN="right"> - <A HREF="<% $baselink. $invlink %>;istax=1" - ><% &$money_sprintf_nonzero( $region->{'tax'} ) %></A> - </TD> - <<%$td%>></TD> - <<%$td%> ALIGN="right"> - <A HREF="<% $creditlink. $invlink %>;istax=1" - ><% &$money_sprintf_nonzero( $region->{'credit'} ) %></A> - </TD> - <<%$td%> COLSPAN=2></TD> -% } else { #not $out - <<%$td%> ALIGN="right"> - <A HREF="<% $baselink. $link %>;istax=1" - ><% &$money_sprintf( $region->{'tax'} ) %></A> - </TD> - <<%$td%>><FONT SIZE="+1"><B> - </B></FONT></TD> - <<%$tdh%> ALIGN="right"> - <A HREF="<% $creditlink. $invlink %>;istax=1" - ><% &$money_sprintf( $region->{'credit'} ) %></A> - </TD> - <<%$td%>><FONT SIZE="+1"><B> = </B></FONT></TD> - <<%$tdh%> ALIGN="right"> - <% &$money_sprintf( $region->{'tax'} - $region->{'credit'} ) %> - </TD> - </TR> -% } # if $out -% } #foreach $region -% } #foreach $class - - </TABLE> - -% } # if show_taxclasses - -<% include('/elements/footer.html') %> - +<& /elements/footer.html &> <%init> die "access denied" @@ -287,17 +188,38 @@ my $DEBUG = $cgi->param('debug') || 0; my $conf = new FS::Conf; -my $out = 'Out of taxable region(s)'; +my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi); -my %label_opt = ( out => 1 ); #enable 'Out of Taxable Region' label -$label_opt{with_city} = 1 if $cgi->param('show_cities'); -$label_opt{with_district} = 1 if $cgi->param('show_districts'); +my ($taxname, $country, %breakdown); -$label_opt{with_taxclass} = 1 if $cgi->param('show_taxclasses'); +if ( $cgi->param('taxname') =~ /^([\w\s]+)$/ ) { + $taxname = $1; +} else { + die "taxname required"; # UI prevents this +} -my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi); +if ( $cgi->param('country') =~ /^(\w\w)$/ ) { + $country = $1; +} else { + die "country required"; +} -my $join_cust = ' JOIN cust_bill USING ( invnum ) +# %breakdown: short name => field identifier +foreach ($cgi->param('breakdown')) { + if ( $_ eq 'taxclass' ) { + $breakdown{'taxclass'} = 'part_pkg.taxclass'; + } elsif ( $_ eq 'pkgclass' ) { + $breakdown{'pkgclass'} = 'part_pkg.classnum'; + } elsif ( $_ eq 'city' ) { + $breakdown{'city'} = 'cust_main_county.city'; + $breakdown{'district'} = 'cust_main_county.district'; + } +} +# always break these down +$breakdown{'state'} = 'cust_main_county.state'; +$breakdown{'county'} = 'cust_main_county.county'; + +my $join_cust = ' JOIN cust_bill USING ( invnum ) LEFT JOIN cust_main USING ( custnum ) '; my $join_cust_pkg = $join_cust. @@ -306,7 +228,7 @@ my $join_cust_pkg = $join_cust. my $from_join_cust_pkg = " FROM cust_bill_pkg $join_cust_pkg "; -my $with_pkgclass = $cgi->param('show_pkgclasses'); +# all queries MUST be linked to both cust_bill and cust_main_county # either or both of these can be used to link cust_bill_pkg to cust_main_county my $pkg_tax = "SELECT SUM(amount) as tax_amount, invnum, taxnum, ". @@ -317,21 +239,32 @@ my $pkg_tax = "SELECT SUM(amount) as tax_amount, invnum, taxnum, ". my $pkg_tax_exempt = "SELECT SUM(amount) AS exempt_charged, billpkgnum, taxnum ". "FROM cust_tax_exempt_pkg EXEMPT_WHERE GROUP BY billpkgnum, taxnum"; -my $where = "WHERE _date >= $beginning AND _date <= $ending "; +my $where = "WHERE _date >= $beginning AND _date <= $ending ". + "AND COALESCE(cust_main_county.taxname,'Tax') = '$taxname' ". + "AND cust_main_county.country = '$country'"; # SELECT/GROUP clauses for first-level queries -# classnum is a placeholder; they all go in one class in this case. -my $select = "SELECT NULL AS classnum, cust_main_county.taxnum, "; -my $group = "GROUP BY cust_main_county.taxnum"; +my $select = "SELECT "; +my $group = "GROUP BY "; +foreach (qw(pkgclass taxclass state county city district)) { + if ( $breakdown{$_} ) { + $select .= "$breakdown{$_} AS $_, "; + $group .= "$breakdown{$_}, "; + } else { + $select .= "NULL AS $_, "; + } +} +$select .= "array_to_string(array_agg(DISTINCT(cust_main_county.taxnum)), ',') AS taxnums, "; +$group =~ s/, $//; + # SELECT/GROUP clauses for second-level (totals) queries -my $select_all = "SELECT NULL AS classnum, "; -my $group_all = ""; - -if ( $with_pkgclass ) { - $select = "SELECT COALESCE(part_pkg.classnum,0), cust_main_county.taxnum, "; - $group = "GROUP BY part_pkg.classnum, cust_main_county.taxnum"; - $select_all = "SELECT COALESCE(part_pkg.classnum,0), "; - $group_all = "GROUP BY COALESCE(part_pkg.classnum,0)"; +# breakdown by package class only, if anything +my $select_all = "SELECT NULL AS pkgclass, "; +my $group_all = ""; +if ( $breakdown{pkgclass} ) { + $select_all = "SELECT $breakdown{pkgclass} AS pkgclass, "; + $group_all = "GROUP BY $breakdown{pkgclass}"; } +$select_all .= "array_to_string(array_agg(DISTINCT(cust_main_county.taxnum)), ',') AS taxnums, "; my $agentname = ''; if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { @@ -356,7 +289,8 @@ my $exempt = "$select SUM(exempt_charged) JOIN ($pkg_tax_exempt) AS pkg_tax_exempt USING (taxnum) JOIN cust_bill_pkg USING (billpkgnum) - $join_cust_pkg $where AND $nottax $group"; + $join_cust_pkg $where AND $nottax + $group"; my $all_exempt = "$select_all SUM(exempt_charged) FROM cust_main_county @@ -393,24 +327,19 @@ $sql{taxable} = "$select LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt ON (pkg_tax_exempt.billpkgnum = cust_bill_pkg.billpkgnum AND pkg_tax_exempt.taxnum = cust_main_county.taxnum) - $join_cust_pkg $where AND $nottax $group"; + $join_cust_pkg $where AND $nottax + $group"; -# Here we're going to sum all line items that are taxable _at all_, -# under any tax. exempt_charged is the sum of all exemptions for a -# particular billpkgnum + taxnum; we take the taxnum that has the -# smallest sum of exemptions and subtract that from the charged amount. $all_sql{taxable} = "$select_all - SUM(cust_bill_pkg.setup + cust_bill_pkg.recur - COALESCE(min_exempt, 0)) - FROM cust_bill_pkg - JOIN ( - SELECT invnum, pkgnum, MIN(exempt_charged) AS min_exempt - FROM ($pkg_tax) AS pkg_tax - JOIN cust_bill_pkg USING (invnum, pkgnum) - LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt USING (billpkgnum, taxnum) - GROUP BY invnum, pkgnum - ) AS pkg_is_taxable - USING (invnum, pkgnum) - $join_cust_pkg $where AND $nottax $group_all"; + SUM(cust_bill_pkg.setup + cust_bill_pkg.recur - COALESCE(exempt_charged, 0)) + FROM cust_main_county + JOIN ($pkg_tax) AS pkg_tax USING (taxnum) + JOIN cust_bill_pkg USING (invnum, pkgnum) + LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt + ON (pkg_tax_exempt.billpkgnum = cust_bill_pkg.billpkgnum + AND pkg_tax_exempt.taxnum = cust_main_county.taxnum) + $join_cust_pkg $where AND $nottax + $group_all"; $sql{taxable} =~ s/EXEMPT_WHERE//; # unrestricted $all_sql{taxable} =~ s/EXEMPT_WHERE//; @@ -428,7 +357,7 @@ my $taxfrom = " FROM cust_bill_pkg LEFT JOIN cust_bill_pkg_tax_location USING ( billpkgnum ) LEFT JOIN cust_main_county USING ( taxnum )"; -if ( $with_pkgclass ) { +if ( $breakdown{pkgclass} ) { # If we're not grouping by package class, this is unnecessary, and # probably really expensive. $taxfrom .= " @@ -439,17 +368,14 @@ if ( $with_pkgclass ) { } my $istax = "cust_bill_pkg.pkgnum = 0"; -my $named_tax = - "COALESCE(taxname,'Tax') = COALESCE(cust_bill_pkg.itemdesc,'Tax')"; $sql{tax} = "$select SUM(cust_bill_pkg_tax_location.amount) $taxfrom - $where AND $istax AND $named_tax + $where AND $istax $group"; -$all_sql{tax} = "$select_all SUM(cust_bill_pkg.setup) - FROM cust_bill_pkg - $join_cust +$all_sql{tax} = "$select_all SUM(cust_bill_pkg_tax_location.amount) + $taxfrom $where AND $istax $group_all"; @@ -463,325 +389,86 @@ my $creditwhere = $where . $sql{credit} = "$select SUM(cust_credit_bill_pkg.amount) $creditfrom - $creditwhere AND $istax AND $named_tax + $creditwhere AND $istax $group"; $all_sql{credit} = "$select_all SUM(cust_credit_bill_pkg.amount) - FROM cust_credit_bill_pkg - JOIN cust_bill_pkg USING (billpkgnum) - $join_cust - $where AND $istax + $creditfrom + $creditwhere AND $istax $group_all"; -if ( $with_pkgclass ) { - # the slightly more complicated version, with lots of joins that are - # unnecessary if you're not breaking down by package class - $all_sql{tax} = "$select_all SUM(cust_bill_pkg_tax_location.amount) - $taxfrom - $where AND $istax - $group_all"; - - $all_sql{credit} = "$select_all SUM(cust_credit_bill_pkg.amount) - $creditfrom - $creditwhere AND $istax - $group_all"; -} - -# "out of taxable region" sales -$all_sql{out_sales} = - "$select_all SUM(cust_bill_pkg.setup + cust_bill_pkg.recur) - FROM (cust_bill_pkg $join_cust_pkg) - LEFT JOIN ($pkg_tax) AS pkg_tax USING (invnum, pkgnum) - LEFT JOIN ($pkg_tax_exempt) AS pkg_tax_exempt USING (billpkgnum) - $where AND $nottax - AND pkg_tax.taxnum IS NULL AND pkg_tax_exempt.taxnum IS NULL - $group_all" -; - -$all_sql{out_sales} =~ s/EXEMPT_WHERE//; - my %data; my %total; +my %taxclass_name = { '' => '' }; +if ( $breakdown{taxclass} ) { + $taxclass_name{$_->taxclassnum} = $_->taxclass + foreach qsearch('tax_class'); + $taxclass_name{''} = 'Unclassified'; +} foreach my $k (keys(%sql)) { my $stmt = $sql{$k}; warn "\n".uc($k).":\n".$stmt."\n" if $DEBUG; my $sth = dbh->prepare($stmt); - # three columns: classnum, taxnum, value + # eight columns: pkgclass, taxclass, state, county, city, district + # taxnums (comma separated), value + # *sigh* $sth->execute or die "failed to execute $k query: ".$sth->errstr; while ( my $row = $sth->fetchrow_arrayref ) { - $data{$k}{$row->[0]}{$row->[1]} = $row->[2]; + my $bin = $data + {$row->[0]} + {$taxclass_name{$row->[1]}} + {$row->[2]} + {$row->[3] ? $row->[3] . ' County' : ''} + {$row->[4]} + {$row->[5]} + ||= []; + push @$bin, [ $k, $row->[6], $row->[7] ]; } } warn "DATA:\n".Dumper(\%data) if $DEBUG > 1; foreach my $k (keys %all_sql) { - warn "\n".$all_sql{$k}."\n" if $DEBUG; + warn "\nTOTAL ".uc($k).":\n".$all_sql{$k}."\n" if $DEBUG; my $sth = dbh->prepare($all_sql{$k}); - # two columns: classnum, value + # three columns: pkgclass, taxnums (comma separated), value $sth->execute or die "failed to execute $k totals query: ".$sth->errstr; while ( my $row = $sth->fetchrow_arrayref ) { - $total{$k}{$row->[0]} = $row->[1]; - } -} -warn "TOTALS:\n".Dumper(\%total);# if $DEBUG > 1; -# so $data{tax}, for example, is now a hash with one entry -# for each classnum, containing a hash with one entry for each -# taxnum, containing the tax billed on that taxnum. -# if with_pkgclass is off, then the classnum is always null. - -# integrity checks -# unlinked tax collected -my $out_tax_sql = - "SELECT SUM(cust_bill_pkg.setup) - FROM (cust_bill_pkg $join_cust) - LEFT JOIN cust_bill_pkg_tax_location USING (billpkgnum) - $where AND $istax AND cust_bill_pkg_tax_location.billpkgnum IS NULL" -; -my $unlinked_tax = FS::Record->scalar_sql($out_tax_sql); -# unlinked tax credited -my $out_credit_sql = - "SELECT SUM(cust_credit_bill_pkg.amount) - FROM cust_credit_bill_pkg - JOIN cust_bill_pkg USING (billpkgnum) - $join_cust - $where AND $istax AND cust_credit_bill_pkg.billpkgtaxlocationnum IS NULL" -; -my $unlinked_credit = FS::Record->scalar_sql($out_credit_sql); - -# all sales -my $all_sales = FS::Record->scalar_sql( - "SELECT SUM(cust_bill_pkg.setup + cust_bill_pkg.recur) - FROM cust_bill_pkg $join_cust $where AND $nottax" -); - -#tax-report_groups filtering -my($group_op, $group_value) = ( '', '' ); -if ( $cgi->param('report_group') =~ /^(=|!=) (.*)$/ ) { - ( $group_op, $group_value ) = ( $1, $2 ); -} -my $group_test = sub { # to be applied to a tax label - my $label = shift; - return 1 unless $group_op; #in case we get called inadvertantly - if ( $label eq $out ) { #don't display "out of taxable region" in this case - 0; - } elsif ( $group_op eq '=' ) { - $label =~ /^$group_value/; - } elsif ( $group_op eq '!=' ) { - $label !~ /^$group_value/; - } else { - die "guru meditation #00de: group_op $group_op\n"; + my $bin = $total{$row->[0]} ||= []; + push @$bin, [ $k, $row->[1], $row->[2] ]; } -}; - -my @pkgclasses; -if ($with_pkgclass) { - @pkgclasses = qsearch('pkg_class', {}); - push @pkgclasses, FS::pkg_class->new({ - classnum => '0', - classname => 'Unclassified', - }); -} else { - @pkgclasses = ( FS::pkg_class->new({ - classnum => '', - classname => '', - }) ); } -my %pkgclass_data; - -foreach my $class (@pkgclasses) { - my $classnum = $class->classnum; - my $classname = $class->classname; - - # if show_taxclasses is on, %base_regions will contain the same data - # as %regions, but with taxclasses merged together (and ignoring report_group - # filtering). - my (%regions, %base_regions); - - my @loc_params = qw(country state county); - push @loc_params, 'city' if $cgi->param('show_cities'); - push @loc_params, 'district' if $cgi->param('show_districts'); - - foreach my $r ( qsearch({ 'table' => 'cust_main_county', })) { - my $taxnum = $r->taxnum; - # set up a %regions entry for this region's tax label - my $label = $r->label(%label_opt); - next if $label eq $out; - $regions{$label} ||= { label => $label }; - - $regions{$label}->{$_} = $r->get($_) foreach @loc_params; - $regions{$label}->{taxnums} ||= []; - push @{ $regions{$label}->{taxnums} }, $r->taxnum; - - my %x; # keys are data items (like 'tax', 'exempt_cust', etc.) - foreach my $k (keys %data) { - next unless exists($data{$k}{$classnum}{$taxnum}); - $x{$k} = $data{$k}{$classnum}{$taxnum}; - $regions{$label}{$k} += $x{$k}; - if ( $k eq 'taxable' or $k =~ /^exempt/ ) { - $regions{$label}->{'sales'} += $x{$k}; - } - } - - my $owed = $data{'taxable'}{$classnum}{$taxnum} * ($r->tax/100); - $regions{$label}->{'owed'} += $owed; - $total{'owed'}{$classnum} += $owed; - - if ( defined($regions{$label}->{'rate'}) - && $regions{$label}->{'rate'} != $r->tax.'%' ) { - $regions{$label}->{'rate'} = 'variable'; - } else { - $regions{$label}->{'rate'} = $r->tax.'%'; - } - - if ( $cgi->param('show_taxclasses') ) { - my $base_label = $r->label(%label_opt, 'with_taxclass' => 0); - $base_regions{$base_label} ||= - { - label => $base_label, - tax => 0, - credit => 0, - }; - $base_regions{$base_label}->{tax} += $x{tax}; - $base_regions{$base_label}->{credit} += $x{credit}; - } +warn "TOTALS:\n".Dumper(\%total) if $DEBUG > 1; - } - - my @regions = map { $_->{label} } - sort { - ($b eq $out) <=> ($a eq $out) - or $a->{country} cmp $b->{country} - or $a->{state} cmp $b->{state} - or $a->{county} cmp $b->{county} - or $a->{city} cmp $b->{city} - } - grep { $_->{sales} > 0 or $_->{tax} > 0 or $_->{credit} > 0 } - values %regions; - - #tax-report_groups filtering - @regions = grep &{$group_test}($_), @regions - if $group_op; - - #calculate totals - my %taxclasses = (); - my %county = (); - my %state = (); - my %country = (); - foreach my $label (@regions) { - $taxclasses{$regions{$_}->{'taxclass'}} = 1 - if $regions{$_}->{'taxclass'}; - $county{$regions{$_}->{'county'}} = 1; - $state{$regions{$_}->{'state'}} = 1; - $country{$regions{$_}->{'country'}} = 1; - } - - my $total_url_param = ''; - my $total_url_param_invoiced = ''; - if ( $group_op ) { - - my @country = keys %country; - warn "WARNING: multiple countries on this grouped report; total links broken" - if scalar(@country) > 1; - my $country = $country[0]; - - my @state = keys %state; - warn "WARNING: multiple countries on this grouped report; total links broken" - if scalar(@state) > 1; - my $state = $state[0]; - - $total_url_param_invoiced = - $total_url_param = - 'report_group='.uri_escape("$group_op $group_value").';'. - join(';', map 'taxclass='.uri_escape($_), keys %taxclasses ); - $total_url_param .= ';'. - "country=$country;state=".uri_escape($state).';'. - join(';', map 'county='.uri_escape($_), keys %county ) ; - - } - - #ordering - @regions = - map $regions{$_}, - sort { $a cmp $b } - @regions; - - my @base_regions = - map $base_regions{$_}, - sort { $a cmp $b } - keys %base_regions; - - #add "Out of taxable" and total lines - if ( $total{out_sales}{$classnum} ) { - my %out = ( - 'sales' => $total{out_sales}{$classnum}, - 'label' => $out, - 'rate' => '' - ); - push @regions, \%out; - push @base_regions, \%out; - } - - if ( @regions ) { - my %class_total = map { $_ => $total{$_}{$classnum} } keys(%total); - $class_total{is_total} = 1; - $class_total{sales} = sum( - @class_total{ 'taxable', - 'out_sales', - grep(/^exempt/, keys %class_total) } - ); - - push @regions, \%class_total; - push @base_regions, \%class_total; - } - - $pkgclass_data{$classname} = { - classnum => $classnum, - classname => $classname, - regions => \@regions, - base_regions => \@base_regions, - }; -} - -if ( $with_pkgclass ) { - my $class_zero = delete( $pkgclass_data{'Unclassified'} ); - @pkgclasses = map { $pkgclass_data{$_} } - sort { $a cmp $b } - keys %pkgclass_data; - push @pkgclasses, $class_zero; - - my %grand_total = map { - $_ => sum( values(%{ $total{$_} }) ) - } keys(%total); - - $grand_total{sales} = $all_sales; - - push @pkgclasses, { - classnum => '', - classname => 'Total', - regions => [ \%grand_total ], - base_regions => [ \%grand_total ], - } -} else { - @pkgclasses = $pkgclass_data{''}; -} - -#-- +# $data{$pkgclass}{$taxclass}{$state}{$county}{$city}{$district} = [ +# [ 'taxable', taxnums, amount ], +# [ 'exempt_cust', taxnums, amount ], +# ... +# ] +# non-requested grouping levels simply collapse into key = '' my $money_char = $conf->config('money_char') || '$'; my $money_sprintf = sub { $money_char. sprintf('%.2f', shift ); }; -my $money_sprintf_nonzero = sub { - $_[0] == 0 ? '' : &$money_sprintf($_[0]) -}; my $dateagentlink = "begin=$beginning;end=$ending"; $dateagentlink .= ';agentnum='. $cgi->param('agentnum') if length($agentname); -my $baselink = $p. "search/cust_bill_pkg.cgi?$dateagentlink"; +my $saleslink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;nottax=1"; +my $taxlink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;istax=1"; my $exemptlink = $p. "search/cust_tax_exempt_pkg.cgi?$dateagentlink"; -my $creditlink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;credit=1"; +my $creditlink = $p. "search/cust_bill_pkg.cgi?$dateagentlink;credit=1;istax=1"; + +my %taxrates; +foreach my $tax ( + qsearch('cust_main_county', { + country => $country, + tax => { op => '>', value => 0 } + }) ) + { + $taxrates{$tax->taxnum} = $tax->tax; +} </%init> diff --git a/httemplate/search/report_tax.html b/httemplate/search/report_tax.html index 8a207aafb..20aa07fc8 100755 --- a/httemplate/search/report_tax.html +++ b/httemplate/search/report_tax.html @@ -4,68 +4,34 @@ <TABLE> -% if ( $conf->config('tax-report_groups') ) { -% my @lines = $conf->config('tax-report_groups'); - - <TR> - <TD ALIGN="right">Tax group</TD> - <TD> - <SELECT NAME="report_group"> - - <OPTION VALUE="">all</OPTION> - -% foreach my $line ( @lines ) { -% $line =~ /^\s*(.+)\s+(=|!=)\s+(.*)\s*$/ #or next; -% or do { warn "bad report_group line: $line\n"; next; }; -% my($label, $op, $value) = ($1, $2, $3); - - <OPTION VALUE="<% "$op $value" %>"><% $label %></OPTION> -% } - - </SELECT> - </TD> - </TR> - -% } - - <% include( '/elements/tr-select-agent.html', 'disable_empty'=>0 ) %> - - <% include( '/elements/tr-input-beginning_ending.html' ) %> - -% if ( $city ) { - <TR> - <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_cities" VALUE="1" onclick="toggle_show_cities(this)"></TD> - <TD>Show cities</TD> - </TR> - <TR> - <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_districts" VALUE="1" DISABLED></TD> - <TD>Show districts</TD> - </TR> - <SCRIPT TYPE="text/javascript"> - function toggle_show_cities() { - what = document.getElementsByName('show_cities')[0]; - what.form.show_districts.disabled = !what.checked; - what.form.show_districts.checked = what.checked; - } - toggle_show_cities(); - </SCRIPT> -% } - -% if ( $conf->exists('enable_taxclasses') ) { - <TR> - <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_taxclasses" VALUE="1"></TD> - <TD>Show tax classes</TD> - </TR> -% } - -% my @pkg_class = qsearch('pkg_class', {}); -% if ( @pkg_class ) { - <TR> - <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="show_pkgclasses" VALUE="1"></TD> - <TD>Show package classes</TD> - </TR> -% } - + <& /elements/tr-select-agent.html, 'disable_empty'=>0 &> + + <& /elements/tr-input-beginning_ending.html &> + + <& /elements/tr-select.html, + 'label' => 'Country', + 'field' => 'country', + 'options' => \@countries, + 'curr_value' => ($conf->config('countrydefault') || 'US'), + &> + + <& /elements/tr-select.html, + 'label' => 'For tax named ', + 'field' => 'taxname', + 'options' => \@taxnames, + 'disable_empty' => 1, + &> + + <& /elements/tr-checkbox-multiple.html, + 'label' => 'Break down by ', + 'field' => 'breakdown', + 'options' => \@breakdown, + 'option_labels' => { + taxclass => 'Tax class', + pkgclass => 'Package class', + city => 'City', + }, + &> </TABLE> <BR><INPUT TYPE="submit" VALUE="Get Report"> @@ -80,12 +46,23 @@ die "access denied" my $conf = new FS::Conf; -my $city_sql = "SELECT COUNT(*) FROM cust_main_county - WHERE city != '' AND city IS NOT NULL - LIMIT 1"; - -my $city_sth = dbh->prepare($city_sql) or die dbh->errstr; -$city_sth->execute or die $city_sth->errstr; -my $city = $city_sth->fetchrow_arrayref->[0]; +my $sth = dbh->prepare('SELECT DISTINCT(COALESCE(taxname, \'Tax\')) FROM cust_main_county'); +$sth->execute or die $sth->errstr; +my @taxnames = map { $_->[0] } @{ $sth->fetchall_arrayref }; + +$sth = dbh->prepare('SELECT DISTINCT(country) FROM cust_location'); +$sth->execute or die $sth->errstr; +my @countries = map { $_->[0] } @{ $sth->fetchall_arrayref }; + +my @breakdown; +if ( $conf->exists('enable_taxclasses') ) { + push @breakdown, 'taxclass'; +} +if ( FS::pkg_class->count() > 0 ) { + push @breakdown, 'pkgclass'; +} +if ( FS::cust_main_county->count("city is not null and city != ''") > 0 ) { + push @breakdown, 'city'; +} </%init> diff --git a/httemplate/search/sales_commission.html b/httemplate/search/sales_commission.html index b4d40aeb2..e74f3792e 100644 --- a/httemplate/search/sales_commission.html +++ b/httemplate/search/sales_commission.html @@ -36,6 +36,8 @@ my $count_query = "SELECT COUNT(*) FROM sales"; my $salesnum; if ( $cgi->param('salesnum') =~ /^(\d+)$/ ) { $salesnum = $1; +} else { + $cgi->delete('salesnum'); } my $title = 'Sales person commission'; @@ -43,13 +45,14 @@ $title .= ': '. time2str($date_format, $beginning). ' to '. time2str($date_format, $ending) if $beginning; +my $paid = $cgi->param('paid') ? 1 : 0; +$title .= ' - paid sales only' if $paid; + my $cust_main_sales = $cgi->param('cust_main_sales') eq 'Y' ? 'Y' : ''; my $sales_link = [ 'sales_pkg_class.html?'. - "begin=$beginning;". - "end=$ending;". - "cust_main_sales=$cust_main_sales;". - "salesnum=", + # pass all of our parameters along + $cgi->query_string. ';salesnum=', 'salesnum' ]; @@ -64,6 +67,7 @@ my $sales_sub_maker = sub { $beginning, $ending, 'cust_main_sales' => $cust_main_sales, + 'paid' => $paid, ); $total += $_->get($field) foreach @cust_bill_pkg; diff --git a/httemplate/search/sales_pkg_class.html b/httemplate/search/sales_pkg_class.html index da5d512d5..8bb6bde4c 100644 --- a/httemplate/search/sales_pkg_class.html +++ b/httemplate/search/sales_pkg_class.html @@ -9,7 +9,7 @@ $sales_sub_maker->('setup'), $sales_sub_maker->('recur'), $commission_sub, ], - 'links' => [ '', '', '', $commission_link ], + 'links' => [ '', $sales_link, $sales_link, $commission_link ], 'align' => 'lrrr', 'query' => { 'table' => 'sales_pkg_class', 'hashref' => { 'salesnum' => $salesnum }, @@ -40,6 +40,9 @@ $title .= ': '. time2str($date_format, $beginning). ' to '. if $beginning; my $cust_main_sales = $cgi->param('cust_main_sales') eq 'Y' ? 'Y' : ''; +my $paid = $cgi->param('paid') ? 1 : 0; + +$title .= " - paid sales only" if $paid; my $sales_link = [ 'cust_bill_pkg.cgi?'. "begin=$beginning;". @@ -55,18 +58,17 @@ my $sales_sub_maker = sub { my $field = shift; sub { my $sales_pkg_class = shift; - - #efficiency improvement: ask the db for a sum instead of all the records - my $total = 0; - my @cust_bill_pkg = $sales->cust_bill_pkg( + # could be even more efficient but this is pretty good + my $search = $sales->cust_bill_pkg_search( $beginning, $ending, 'cust_main_sales' => $cust_main_sales, 'classnum' => $sales_pkg_class->classnum, + 'paid' => $paid, ); - $total += $_->get($field) foreach @cust_bill_pkg; - - $money_char. sprintf('%.2f', $total); + $search->{'select'} = "SUM(cust_bill_pkg.$field) AS total"; + my $result = qsearchs($search); + $money_char. sprintf('%.2f', $result ? $result->get('total') : 0); }; }; @@ -85,6 +87,15 @@ my $commission_sub = sub { $money_char. sprintf('%.2f', $total_credit); }; +my $sales_link = [ 'cust_bill_pkg.cgi?'. + "begin=$beginning;". + "end=$ending;". + "cust_main_sales=$cust_main_sales;". + "salesnum=$salesnum;". + "classnum=", + 'classnum' + ]; + my $commission_link = [ 'cust_credit.html?'. "begin=$beginning;". "end=$ending;". diff --git a/httemplate/view/cust_main/billing.html b/httemplate/view/cust_main/billing.html index e286305f4..e0dd7b904 100644 --- a/httemplate/view/cust_main/billing.html +++ b/httemplate/view/cust_main/billing.html @@ -221,7 +221,10 @@ % if ( $conf->exists('enable_taxproducts') ) { <TR> <TD ALIGN="right"><% mt('Tax location') |h %></TD> - <TD BGCOLOR="#ffffff"><% $cust_main->geocode('cch') %></TD> +% my $tax_location = $conf->exists('tax-ship_address') +% ? $cust_main->ship_location +% : $cust_main->bill_location; + <TD BGCOLOR="#ffffff"><% $tax_location->geocode('cch') %></TD> </TR> % } <TR> diff --git a/httemplate/view/cust_main/contacts_new.html b/httemplate/view/cust_main/contacts_new.html index 63a050c04..a851d99ed 100644 --- a/httemplate/view/cust_main/contacts_new.html +++ b/httemplate/view/cust_main/contacts_new.html @@ -22,7 +22,7 @@ % }) % or next; <TD ALIGN="right"> <% $phone_type->typename %> phone</TD> - <TD BGCOLOR="#FFFFFF"><% $contact_phone->phonenum |h %></TD> + <TD BGCOLOR="#FFFFFF"><% $contact_phone->phonenum_pretty |h %></TD> % } </TR> diff --git a/httemplate/view/svc_external.cgi b/httemplate/view/svc_external.cgi index 077dc77b7..833e86247 100644 --- a/httemplate/view/svc_external.cgi +++ b/httemplate/view/svc_external.cgi @@ -23,6 +23,7 @@ </TABLE></TD></TR></TABLE> +<BR> <& /elements/table-tickets.html, object => $cust_svc &> |