summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FS/FS.pm6
-rw-r--r--FS/FS/Conf.pm7
-rw-r--r--FS/FS/Schema.pm12
-rw-r--r--FS/FS/cust_main.pm7
-rw-r--r--FS/FS/cust_pkg.pm32
-rw-r--r--FS/FS/part_pkg_report_option.pm125
-rw-r--r--FS/MANIFEST2
-rw-r--r--FS/t/part_pkg_report_option.t5
-rw-r--r--httemplate/browse/part_pkg_report_option.html28
-rw-r--r--httemplate/edit/cust_main/bottomfixup.html7
-rw-r--r--httemplate/edit/cust_main/bottomfixup.js179
-rw-r--r--httemplate/edit/cust_main/choose_tax_location.html2
-rw-r--r--httemplate/edit/cust_main/contact.html1
-rwxr-xr-xhttemplate/edit/part_pkg.cgi28
-rw-r--r--httemplate/edit/part_pkg_report_option.html23
-rwxr-xr-xhttemplate/edit/process/part_pkg.cgi5
-rw-r--r--httemplate/edit/process/part_pkg_report_option.html11
-rw-r--r--httemplate/elements/location.html3
-rw-r--r--httemplate/elements/menu.html5
-rw-r--r--httemplate/misc/xmlhttp-cust_main-censustract.html103
-rwxr-xr-xhttemplate/search/cust_main.html7
-rwxr-xr-xhttemplate/search/report_cust_main.html11
-rwxr-xr-xhttemplate/search/report_cust_pkg.html14
23 files changed, 577 insertions, 46 deletions
diff --git a/FS/FS.pm b/FS/FS.pm
index 1477e985f..c3148735b 100644
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -167,9 +167,9 @@ L<FS::part_export> - External provisioning export class
L<FS::part_export_option> - Export option class
-L<FS::pkg_category> - Package category class
+L<FS::pkg_category> - Package category class (invoice oriented)
-L<FS::pkg_class> - Package class class
+L<FS::pkg_class> - Package class class (tax oriented)
L<FS::part_pkg> - Package definition class
@@ -179,6 +179,8 @@ L<FS::part_pkg_taxclass> - Tax class class
L<FS::part_pkg_option> - Package definition option class
+L<FS::part_pkg_report_option> - Package reporting classification class
+
L<FS::pkg_svc> - Class linking package definitions (see L<FS::part_pkg>) with
service definitions (see L<FS::part_svc>)
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index b45cdd797..12d6075b8 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -2576,6 +2576,13 @@ worry that config_items is freeside-specific and icky.
},
{
+ 'key' => 'cust_main-require_censustract',
+ 'section' => 'UI',
+ 'description' => 'Customer is required to have a census tract. Useful for FCC form 477 reports. See also: cust_main-auto_standardize_address',
+ 'type' => 'checkbox',
+ },
+
+ {
'key' => 'disable_acl_changes',
'section' => '',
'description' => 'Disable all ACL changes, for demos.',
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index aed8d6079..61cd17e06 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -686,6 +686,7 @@ sub tables_hashref {
'paytype', 'varchar', 'NULL', $char_d, '', '',
'payip', 'varchar', 'NULL', 15, '', '',
'geocode', 'varchar', 'NULL', 20, '', '',
+ 'censustract', 'varchar', 'NULL', 20, '', '', # 7 to save space?
'tax', 'char', 'NULL', 1, '', '',
'otaker', 'varchar', '', 32, '', '',
'refnum', 'int', '', '', '', '',
@@ -1850,6 +1851,17 @@ sub tables_hashref {
'index' => [ [ 'pkgpart' ], [ 'optionname' ] ],
},
+ 'part_pkg_report_option' => {
+ 'columns' => [
+ 'num', 'serial', '', '', '', '',
+ 'name', 'varchar', '', $char_d, '', '',
+ 'disabled', 'char', 'NULL', 1, '', '',
+ ],
+ 'primary_key' => 'num',
+ 'unique' => [ [ 'name' ] ],
+ 'index' => [ [ 'disabled' ] ],
+ },
+
'rate' => {
'columns' => [
'ratenum', 'serial', '', '', '', '',
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index 3f23346db..e5f289ca3 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -7573,6 +7573,13 @@ sub search_sql {
unless $params->{'cancelled_pkgs'};
##
+ # parse without census tract checkbox
+ ##
+
+ push @where, "(censustract = '' or censustract is null)"
+ if $params->{'no_censustract'};
+
+ ##
# dates
##
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index a510c5245..881e005de 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -2325,12 +2325,44 @@ sub search_sql {
#eslaf
###
+ # parse package report options
+ ###
+
+ my @report_option = ();
+ if ( exists($params->{'report_option'})
+ && $params->{'report_option'} =~ /^([,\d]*)$/
+ )
+ {
+ @report_option = split(',', $1);
+ }
+
+ if (@report_option) {
+ # this will result in the empty set for the dangling comma case as it should
+ push @where,
+ map{ "0 < ( SELECT count(*) FROM part_pkg_option
+ WHERE part_pkg_option.pkgpart = part_pkg.pkgpart
+ AND optionname = 'report_option_$_'
+ AND optionvalue = '1' )"
+ } @report_option;
+ }
+
+ #eslaf
+
+ ###
# parse custom
###
push @where, "part_pkg.custom = 'Y'" if $params->{custom};
###
+ # parse censustract
+ ###
+
+ if ( $params->{'censustract'} =~ /^([.\d]+)$/ and $1 ) {
+ push @where, "cust_main.censustract = '". $params->{censustract}. "'";
+ }
+
+ ###
# parse part_pkg
###
diff --git a/FS/FS/part_pkg_report_option.pm b/FS/FS/part_pkg_report_option.pm
new file mode 100644
index 000000000..16a4c9864
--- /dev/null
+++ b/FS/FS/part_pkg_report_option.pm
@@ -0,0 +1,125 @@
+package FS::part_pkg_report_option;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::part_pkg_report_option - Object methods for part_pkg_report_option records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_report_option;
+
+ $record = new FS::part_pkg_report_option \%hash;
+ $record = new FS::part_pkg_report_option { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_report_option object represents a package definition optional
+reporting classification. FS::part_pkg_report_option inherits from
+FS::Record. The following fields are currently supported:
+
+=over 4
+
+=item num
+
+primary key
+
+=item name
+
+name - The name associated with the reporting option
+
+=item disabled
+
+disabled - set to 'Y' to prevent addition to new packages
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new report option. To add the option to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'part_pkg_report_option'; }
+
+=item insert
+
+Adds this record to the database. If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+ return "Can't delete part_pkg_report_option records!";
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database. If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+=item check
+
+Checks all fields to make sure this is a valid example. If there is
+an error, returns the error, otherwise returns false. Called by the insert
+and replace methods.
+
+=cut
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+ my $self = shift;
+
+ my $error =
+ $self->ut_numbern('num')
+ || $self->ut_text('name')
+ || $self->ut_enum('disabled', [ '', 'Y' ])
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+Overlaps somewhat with pkg_class and pkg_category
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 70287a3d9..7def8dffd 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -439,6 +439,8 @@ FS/cust_bill_pkg_tax_rate_location.pm
t/cust_bill_pkg_tax_rate_location.t
FS/cust_recon.pm
t/cust_recon.t
+FS/part_pkg_report_option.pm
+t/part_pkg_report_option.t
FS/cust_main_exemption.pm
t/cust_main_exemption.t
FS/cust_tax_adjustment.pm
diff --git a/FS/t/part_pkg_report_option.t b/FS/t/part_pkg_report_option.t
new file mode 100644
index 000000000..622bb3872
--- /dev/null
+++ b/FS/t/part_pkg_report_option.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::part_pkg_report_option;
+$loaded=1;
+print "ok 1\n";
diff --git a/httemplate/browse/part_pkg_report_option.html b/httemplate/browse/part_pkg_report_option.html
new file mode 100644
index 000000000..90540bca2
--- /dev/null
+++ b/httemplate/browse/part_pkg_report_option.html
@@ -0,0 +1,28 @@
+<% include( 'elements/browse.html',
+ 'title' => 'Package optional report classes',
+ 'html_init' => $html_init,
+ 'name' => 'package optional report classes',
+ 'disableable' => 1,
+ 'disabled_statuspos' => 2,
+ 'query' => { 'table' => 'part_pkg_report_option',
+ 'hashref' => {},
+ 'extra_sql' => 'ORDER BY name',
+ },
+ 'count_query' => 'SELECT COUNT(*) FROM part_pkg_report_option',
+ 'header' => [ '#', 'Class' ],
+ 'fields' => [ 'num', 'name' ],
+ 'links' => [ $link, $link ],
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $html_init =
+ 'Package optional report classes define groups of packages, for reporting purposes.'.
+ qq!<BR><BR><A HREF="${p}edit/part_pkg_report_option.html"><I>Add a class</I></A><BR><BR>!;
+
+my $link = [ $p.'edit/part_pkg_report_option.html?', 'num' ];
+
+</%init>
diff --git a/httemplate/edit/cust_main/bottomfixup.html b/httemplate/edit/cust_main/bottomfixup.html
index 3eb43e0e5..1b29c671a 100644
--- a/httemplate/edit/cust_main/bottomfixup.html
+++ b/httemplate/edit/cust_main/bottomfixup.html
@@ -7,6 +7,13 @@
)
%>
+<% include( '/elements/xmlhttp.html',
+ 'url' => $p.'misc/xmlhttp-cust_main-censustract.html',
+ 'subs' => [ 'censustract' ],
+ #'method' => 'POST', #could get too long?
+ )
+%>
+
<SCRIPT TYPE="text/javascript">
<% include('bottomfixup.js') %>
</SCRIPT>
diff --git a/httemplate/edit/cust_main/bottomfixup.js b/httemplate/edit/cust_main/bottomfixup.js
index ae4aafb70..822f98d39 100644
--- a/httemplate/edit/cust_main/bottomfixup.js
+++ b/httemplate/edit/cust_main/bottomfixup.js
@@ -86,52 +86,17 @@ function update_address(arg) {
cf.elements['ship_zip'].value = argsHash['new_ship_zip'];
}
- }
-
- var cf = document.CustomerForm;
-
-% if ( $conf->exists('enable_taxproducts') ) {
-
- if ( <% $taxpre %>error ||
- new String(argsHash['new_<% $taxpre %>zip']).length < 10 )
- {
-
- var country_el = cf.elements['<% $taxpre %>country'];
- var country = country_el.options[ country_el.selectedIndex ].value;
-
- if ( country == 'CA' || country == 'US' ) {
-
- var state_el = cf.elements['<% $taxpre %>state'];
- var state = state_el.options[ state_el.selectedIndex ].value;
-
- var url = "cust_main/choose_tax_location.html" +
- "?data_vendor=cch-zip" +
- ";city=" + cf.elements['<% $taxpre %>city'].value +
- ";state=" + state +
- ";zip=" + cf.elements['<% $taxpre %>zip'].value +
- ";country=" + country +
- ";";
-
- // popup a chooser
- OLgetAJAX( url, update_geocode, 300 );
-
- } else {
-
- cf.elements['geocode'].value = 'DEFAULT';
- cf.submit();
+ post_standardization();
- }
+ }
- } else
-% }
if ( changed || ship_changed ) {
% if ( $conf->exists('cust_main-auto_standardize_address') ) {
standardize_address();
- cf.submit();
% } else {
@@ -198,9 +163,9 @@ function update_address(arg) {
confirm_change = confirm_change +
'<TR><TD>' +
- '<BUTTON TYPE="button" onClick="document.CustomerForm.submit();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' +
+ '<BUTTON TYPE="button" onClick="post_standardization();"><IMG SRC="<%$p%>images/error.png" ALT=""> Use entered ' + addresses + '</BUTTON>' +
'</TD><TD>' +
- '<BUTTON TYPE="button" onClick="standardize_address(); document.CustomerForm.submit();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' +
+ '<BUTTON TYPE="button" onClick="standardize_address();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Use standardized ' + addresses + '</BUTTON>' +
'</TD></TR>' +
'<TR><TD COLSPAN=2 ALIGN="center">' +
'<BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' +
@@ -213,10 +178,85 @@ function update_address(arg) {
} else {
- cf.submit();
+ post_standardization();
+
+ }
+
+
+}
+
+function post_standardization() {
+
+ var cf = document.CustomerForm;
+
+% if ( $conf->exists('enable_taxproducts') ) {
+
+ if ( new String(cf.elements['<% $taxpre %>zip'].value).length < 10 )
+ {
+
+ var country_el = cf.elements['<% $taxpre %>country'];
+ var country = country_el.options[ country_el.selectedIndex ].value;
+
+ if ( country == 'CA' || country == 'US' ) {
+
+ var state_el = cf.elements['<% $taxpre %>state'];
+ var state = state_el.options[ state_el.selectedIndex ].value;
+
+ var url = "cust_main/choose_tax_location.html" +
+ "?data_vendor=cch-zip" +
+ ";city=" + cf.elements['<% $taxpre %>city'].value +
+ ";state=" + state +
+ ";zip=" + cf.elements['<% $taxpre %>zip'].value +
+ ";country=" + country +
+ ";";
+
+ // popup a chooser
+ OLgetAJAX( url, update_geocode, 300 );
+
+ } else {
+
+ cf.elements['geocode'].value = 'DEFAULT';
+ post_geocode();
+
+ }
+
+ } else {
+
+ post_geocode();
}
+% } else {
+
+ post_geocode();
+
+% }
+
+}
+
+function post_geocode() {
+
+% if ( $conf->exists('cust_main-require_censustract') ) {
+
+ //alert('fetch census tract data');
+ var cf = document.CustomerForm;
+ var state_el = cf.elements['ship_state'];
+ var census_data = new Array(
+ 'year', '2008', // from config value?
+ 'address', cf.elements['ship_address1'].value,
+ 'city', cf.elements['ship_city'].value,
+ 'state', state_el.options[ state_el.selectedIndex ].value,
+ 'zip', cf.elements['ship_zip'].value
+ );
+
+ censustract( census_data, update_censustract );
+
+% }else{
+
+ document.CustomerForm.submit();
+
+% }
+
}
function update_geocode() {
@@ -232,6 +272,7 @@ function update_geocode() {
setselect(cf.elements['<% $taxpre %>state'], argsHash['state']);
cf.elements['<% $taxpre %>zip'].value = argsHash['zip'];
cf.elements['geocode'].value = argsHash['geocode'];
+ post_geocode();
}
@@ -241,6 +282,64 @@ function update_geocode() {
}
+var set_censustract;
+
+function update_censustract(arg) {
+
+ var argsHash = eval('(' + arg + ')');
+
+ var cf = document.CustomerForm;
+
+ var msacode = argsHash['msacode'];
+ var statecode = argsHash['statecode'];
+ var countycode = argsHash['countycode'];
+ var tractcode = argsHash['tractcode'];
+ var error = argsHash['error'];
+
+ set_censustract = function () {
+
+ cf.elements['censustract'].value =
+ document.forms.popupcensustract.elements.censustract.value;
+ cf.submit();
+
+ }
+
+ if (error) {
+ // popup an entry dialog
+
+ var choose_censustract =
+ '<CENTER><BR><B>Enter census tract</B><BR><BR>' +
+ '<FORM name="popupcensustract">' +
+ '<TABLE>';
+
+ choose_censustract = choose_censustract +
+ '<TR><TH>Census Tract: </TH>' +
+ '<TD><INPUT NAME="censustract" ID="censustract"></TD>' +
+ '</TR><TR>' +
+ '<TD>&nbsp;</TD><TD>&nbsp;</TD></TR>';
+
+ choose_censustract = choose_censustract +
+ '<TR><TD>' +
+ '<BUTTON TYPE="button" onClick="set_censustract();"><IMG SRC="<%$p%>images/tick.png" ALT="">Submit census tract</BUTTON>' +
+ '</TD><TD>' +
+ '<BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission</BUTTON></TD></TR>' +
+ '</TABLE></FORM></CENTER>';
+
+ overlib( choose_censustract, CAPTION, 'Choose a census tract', STICKY, AUTOSTATUSCAP, CLOSETEXT, '', MIDX, 0, MIDY, 0, DRAGGABLE, WIDTH, 576, HEIGHT, 268, BGCOLOR, '#333399', CGCOLOR, '#333399', TEXTSIZE, 3 );
+
+ setTimeout("document.forms.popupcensustract.elements.censustract.focus()",1);
+ } else {
+
+ cf.elements['censustract'].value =
+ new String(statecode) +
+ new String(countycode) +
+ new String(tractcode);
+ cf.submit();
+
+ }
+
+}
+
function copyelement(from, to) {
if ( from == undefined ) {
to.value = '';
diff --git a/httemplate/edit/cust_main/choose_tax_location.html b/httemplate/edit/cust_main/choose_tax_location.html
index 2a4192632..be93a5de8 100644
--- a/httemplate/edit/cust_main/choose_tax_location.html
+++ b/httemplate/edit/cust_main/choose_tax_location.html
@@ -26,7 +26,7 @@
</SELECT><BR><BR>
<TABLE><TR>
- <TD> <BUTTON TYPE="button" onClick="set_geocode(document.getElementById('geocodes')); document.CustomerForm.submit();"><IMG SRC="<%$p%>images/tick.png" ALT=""> Set location </BUTTON></TD>
+ <TD> <BUTTON TYPE="button" onClick="set_geocode(document.getElementById('geocodes'));"><IMG SRC="<%$p%>images/tick.png" ALT=""> Set location </BUTTON></TD>
<TD><BUTTON TYPE="button" onClick="document.CustomerForm.submitButton.disabled=false; parent.cClick();"><IMG SRC="<%$p%>images/cross.png" ALT=""> Cancel submission </BUTTON></TD>
</TR>
</TABLE>
diff --git a/httemplate/edit/cust_main/contact.html b/httemplate/edit/cust_main/contact.html
index 27dd38516..2691da652 100644
--- a/httemplate/edit/cust_main/contact.html
+++ b/httemplate/edit/cust_main/contact.html
@@ -32,6 +32,7 @@
'disabled' => $disabled,
'same_checked' => $opt{'same_checked'},
'geocode' => $opt{'geocode'},
+ 'censustract' => $opt{'censustract'},
)
%>
diff --git a/httemplate/edit/part_pkg.cgi b/httemplate/edit/part_pkg.cgi
index 0934f501a..a78aa87d8 100755
--- a/httemplate/edit/part_pkg.cgi
+++ b/httemplate/edit/part_pkg.cgi
@@ -46,6 +46,7 @@
'recur_fee' => 'Recurring fee',
'bill_dst_pkgpart' => 'Include line item(s) from package',
'svc_dst_pkgpart' => 'Include services of package',
+ 'report_option' => 'Report classes',
},
'fields' => [
@@ -161,6 +162,19 @@
{ type => 'columnend' },
+ { 'type' => $census ? 'tablebreak-tr-title'
+ : 'hidden',
+ 'value' => 'Optional report classes',
+ 'field' => 'census_title',
+ },
+ { 'field' => 'report_option',
+ 'type' => $census ? 'select-table' : 'hidden',
+ 'table' => 'part_pkg_report_option',
+ 'name_col' => 'name',
+ 'multiple' => 1,
+ },
+
+
{ 'type' => 'tablebreak-tr-title',
'value' => 'Pricing add-ons',
},
@@ -224,6 +238,7 @@ my $agent_clone_extra_sql =
my $conf = new FS::Conf;
my $taxproducts = $conf->exists('enable_taxproducts');
+my $census = scalar( qsearch( 'part_pkg_report_option', {} ) );
#XXX
# - tr-part_pkg_freq: month_increments_only (from price plans)
@@ -301,14 +316,27 @@ my $edit_callback = sub {
(@agent_type) = map {$_->typenum} qsearch('type_pkgs',{'pkgpart'=>$1});
+ my @report_option = ();
foreach ($object->options) {
/^usage_taxproductnum_(\d+)$/ && ($taxproductnums{$1} = 1);
+ /^report_option_(\d+)$/ && (push @report_option, $1);
}
foreach ($object->part_pkg_taxoverride) {
$taxproductnums{$_->usage_class} = 1
if $_->usage_class;
}
+ $cgi->param('report_option', join(',', @report_option));
+ foreach my $field ( @$fields ) {
+ next unless (
+ ref($field) eq 'HASH' &&
+ $field->{field} &&
+ $field->{field} eq 'report_option'
+ );
+ #$field->{curr_value} = join(',', @report_option);
+ $field->{value} = join(',', @report_option);
+ }
+
%options = $object->options;
$object->set($_ => $object->option($_))
diff --git a/httemplate/edit/part_pkg_report_option.html b/httemplate/edit/part_pkg_report_option.html
new file mode 100644
index 000000000..a6f8e57b7
--- /dev/null
+++ b/httemplate/edit/part_pkg_report_option.html
@@ -0,0 +1,23 @@
+<% include( 'elements/edit.html',
+ 'name' => 'Package optional report class',
+ 'table' => 'part_pkg_report_option',
+ 'fields' => [
+ 'name',
+ { field=>'num', type=>'hidden' },
+ { field=>'disabled', type=>'checkbox', value=>'Y', },
+ ],
+ 'labels' => {
+ 'num' => 'Class number',
+ 'name' => 'Class name',
+ 'disabled' => 'Disable class',
+ },
+ 'viewall_dir' => 'browse',
+ )
+
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/edit/process/part_pkg.cgi b/httemplate/edit/process/part_pkg.cgi
index 3116b7b28..107d45972 100755
--- a/httemplate/edit/process/part_pkg.cgi
+++ b/httemplate/edit/process/part_pkg.cgi
@@ -102,6 +102,11 @@ my $args_callback = sub {
$options{"usage_taxproductnum_$_"} = $value;
}
+ foreach ( split(',', $cgi->param('report_option') ) ) {
+ $error ||= "Illegal optional report class: $_" unless ( $_ =~ /^\d*$/ );
+ $options{"report_option_$_"} = 1;
+ }
+
$options{$_} = scalar( $cgi->param($_) )
for (qw( setup_fee recur_fee ));
diff --git a/httemplate/edit/process/part_pkg_report_option.html b/httemplate/edit/process/part_pkg_report_option.html
new file mode 100644
index 000000000..052aabd72
--- /dev/null
+++ b/httemplate/edit/process/part_pkg_report_option.html
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+ 'table' => 'part_pkg_report_option',
+ 'viewall_dir' => 'browse',
+ )
+%>
+<%init>
+
+die "access denied"
+ unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+</%init>
diff --git a/httemplate/elements/location.html b/httemplate/elements/location.html
index dbc567d4d..1bdbf9604 100644
--- a/httemplate/elements/location.html
+++ b/httemplate/elements/location.html
@@ -9,6 +9,7 @@ Example:
'disabled' => $disabled,
'same_checked' => $same_checked,
'geocode' => $geocode, #passed through
+ 'censustract' => $censustract, #passed through
'no_asterisks' => 0, #set true to disable the red asterisks next
#to required fields
'address1_label' => 'Address', #label for address
@@ -85,6 +86,8 @@ Example:
% if ( !$pre ) {
<INPUT TYPE="hidden" NAME="geocode" VALUE="<% $opt{geocode} %>">
+% } else {
+ <INPUT TYPE="hidden" NAME="censustract" VALUE="<% $opt{censustract} %>">
% }
<%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 5789a8a3f..cda1efcae 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -170,6 +170,8 @@ if ( $curuser->access_right('Financial reports') ) {
$report_packages{'All customer packages'} = [ $fsurl.'search/cust_pkg.cgi?pkgnum', 'List all customer packages', ];
$report_packages{'Suspended customer packages'} = [ $fsurl.'search/cust_pkg.cgi?magic=suspended', 'List suspended packages' ];
$report_packages{'Customer packages with unconfigured services'} = [ $fsurl.'search/cust_pkg.cgi?APKG_pkgnum', 'List packages which have provisionable services' ];
+$report_packages{'FCC Form 477 packages'} = [ $fsurl.'search/report_477.html', 'Summarize packages by census tract for particular types' ]
+ if $conf->exists('cust_main-require_censustract');
$report_packages{'Advanced package reports'} = [ $fsurl.'search/report_cust_pkg.html', 'by agent, date range, status, package definition' ];
tie my %report_rating, 'Tie::IxHash',
@@ -284,7 +286,8 @@ $config_export_svc_pkg{'View/Edit package definitions'} = [ $fsurl.'browse/part_
|| $curuser->access_right('Edit global package definitions');
if ( $curuser->access_right('Configuration') ) {
$config_export_svc_pkg{'View/Edit package categories'} = [ $fsurl.'browse/pkg_category.html', 'Package categories define groups of package classes, for reporting and convenience purposes.' ];
- $config_export_svc_pkg{'View/Edit package classes'} = [ $fsurl.'browse/pkg_class.html', 'Package classes define groups of packages, for reporting and convenience purposes.' ];
+ $config_export_svc_pkg{'View/Edit package tax classes'} = [ $fsurl.'browse/pkg_class.html', 'Package classes define groups of packages, for taxes, reporting and convenience purposes.' ];
+ $config_export_svc_pkg{'View/Edit package report classes'} = [ $fsurl.'browse/part_pkg_report_option.html', 'Package classes define groups of packages for reporting purposes.' ];
$config_export_svc_pkg{'View/Edit cancel reason types'} = [ $fsurl.'browse/reason_type.html?class=C', 'Cancel reason types define groups of reasons, for reporting and convenience purposes.' ];
$config_export_svc_pkg{'View/Edit cancel reasons'} = [ $fsurl.'browse/reason.html?class=C', 'Cancel reasons explain why a service was cancelled.' ];
$config_export_svc_pkg{'View/Edit suspend reason types'} = [ $fsurl.'browse/reason_type.html?class=S', 'Suspend reason types define groups of reasons, for reporting and convenience purposes.' ];
diff --git a/httemplate/misc/xmlhttp-cust_main-censustract.html b/httemplate/misc/xmlhttp-cust_main-censustract.html
new file mode 100644
index 000000000..05636d3fb
--- /dev/null
+++ b/httemplate/misc/xmlhttp-cust_main-censustract.html
@@ -0,0 +1,103 @@
+<% objToJson($return) %>
+<%init>
+
+my $DEBUG = 0;
+
+my $url='http://www.ffiec.gov/Geocode/default.aspx';
+
+my $sub = $cgi->param('sub');
+
+my $return = {};
+my $error = '';
+
+use LWP::UserAgent;
+use HTTP::Request;
+use HTTP::Request::Common qw( GET POST );
+use HTML::TokeParser;
+
+if ( $sub eq 'censustract' ) {
+
+ my %arg = $cgi->param('arg');
+ warn join('', map "$_: $arg{$_}\n", keys %arg )
+ if $DEBUG;
+
+ my $ua = new LWP::UserAgent;
+ my $res = $ua->request( GET( $url ) );
+
+ warn $res->as_string
+ if $DEBUG > 1;
+
+ unless ($res->code eq '200') {
+
+ $error = $res->message;
+
+ } else {
+
+ my $content = $res->content;
+ my $p = new HTML::TokeParser \$content;
+ my $viewstate;
+ while (my $token = $p->get_tag('input') ) {
+ next unless $token->[1]->{name} eq '__VIEWSTATE';
+ $viewstate = $token->[1]->{value};
+ last;
+ }
+
+ unless ($viewstate) {
+
+ $error = "no __VIEWSTATE found";
+
+ } else {
+
+ my($zip5, $zip4) = split('-',$arg{zip});
+
+ my @ffiec_args = (
+ __VIEWSTATE => $viewstate,
+ ddlbYear => $arg{year},
+ txtAddress => $arg{address},
+ txtCity => $arg{city},
+ ddlbState => $arg{state},
+ txtZipCode => $zip5,
+ btnSearch => 'Search',
+ );
+ warn join("\n", @ffiec_args )
+ if $DEBUG;
+
+ $res = $ua->request( POST( $url, \@ffiec_args ) );
+ warn $res->as_string
+ if $DEBUG > 1;
+
+ unless ($res->code eq '200') {
+
+ $error = $res->message;
+
+ } else {
+
+ my @id = qw( MSACode StateCode CountyCode TractCode );
+ $content = $res->content;
+ $p = new HTML::TokeParser \$content;
+ my $prefix = 'UcGeoResult11_lb';
+ my $compare =
+ sub { my $t=shift; scalar( grep { lc($t) eq lc("$prefix$_")} @id ) };
+
+ while (my $token = $p->get_tag('span') ) {
+ next unless ( $token->[1]->{id} && &$compare( $token->[1]->{id} ) );
+ $token->[1]->{id} =~ /^$prefix(\w+)$/;
+ $return->{lc($1)} = $p->get_trimmed_text("/span");
+ }
+
+ $error = "No census tract found" unless $return->{tractcode};
+
+ } #unless ($res->code eq '200')
+
+ } #unless ($viewstate)
+
+ } #unless ($res->code eq '200')
+
+ $error = "FFIEC Geocoding error: $error" if $error;
+ $return->{'error'} = $error;
+
+ $return;
+
+}
+
+</%init>
diff --git a/httemplate/search/cust_main.html b/httemplate/search/cust_main.html
index 0625a125a..f098fd3a6 100755
--- a/httemplate/search/cust_main.html
+++ b/httemplate/search/cust_main.html
@@ -43,9 +43,12 @@ my %search_hash = ();
#$search_hash{'query'} = $cgi->keywords;
#scalars
-for my $param (qw(
+my @scalars = qw (
agentnum status cancelled_pkgs cust_fields flattened_pkgs custbatch
-)) {
+ no_censustract
+);
+
+for my $param ( @scalars ) {
$search_hash{$param} = scalar( $cgi->param($param) )
if $cgi->param($param);
}
diff --git a/httemplate/search/report_cust_main.html b/httemplate/search/report_cust_main.html
index b0c5fde86..f139d4bb5 100755
--- a/httemplate/search/report_cust_main.html
+++ b/httemplate/search/report_cust_main.html
@@ -56,6 +56,15 @@
<TD><INPUT TYPE="checkbox" NAME="cancelled_pkgs"></TD>
</TR>
+% if ( $conf->exists('cust_main-require_censustract') ) {
+
+ <TR>
+ <TD ALIGN="right" VALIGN="center">Without census tract</TD>
+ <TD><INPUT TYPE="checkbox" NAME="no_censustract"></TD>
+ </TR>
+
+% }
+
<TR>
<TH BGCOLOR="#e8e8e8" COLSPAN=2>&nbsp;</TH>
</TR>
@@ -84,6 +93,8 @@ die "access denied"
$FS::CurrentUser::CurrentUser->access_right('List packages')
);;
+my $conf = new FS::Conf;
+
</%init>
<%once>
diff --git a/httemplate/search/report_cust_pkg.html b/httemplate/search/report_cust_pkg.html
index 3840663cf..66dd7d15e 100755
--- a/httemplate/search/report_cust_pkg.html
+++ b/httemplate/search/report_cust_pkg.html
@@ -72,6 +72,20 @@
)
%>
+% if ( scalar( qsearch( 'part_pkg_report_option', { 'disabled' => '' } ) ) ) {
+
+ <% include( '/elements/tr-select-table.html',
+ 'label' => 'Report classes',
+ 'table' => 'part_pkg_report_option',
+ 'name_col' => 'name',
+ 'hashref' => { 'disabled' => '' },
+ 'element_name' => 'report_option',
+ 'multiple' => 'multiple',
+ )
+ %>
+
+% }
+
% foreach my $field (qw( setup last_bill bill adjourn susp expire cancel )) {
<TR>