From 6b12c14cc10503d6b0783e8ef71fe44d9a9b37b6 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 19 Jun 2006 08:05:28 +0000 Subject: [PATCH] add ability to select specific package defs. and package status to package report for qis --- FS/FS/Conf.pm | 29 +++-- FS/FS/ConfDefaults.pm | 68 +++++++++++ FS/FS/UI/Web.pm | 126 ++++++++++++++------- FS/FS/cust_main_Mixin.pm | 44 +++++++ FS/FS/cust_pkg.pm | 76 ++++++++++--- httemplate/config/config.cgi | 72 +++++++++--- httemplate/elements/menu.html | 4 +- httemplate/elements/select-cust-fields.html | 23 ++++ httemplate/elements/select-cust_pkg-status.html | 19 ++++ httemplate/elements/tr-select-cust-fields.html | 14 +++ httemplate/elements/tr-select-cust_pkg-status.html | 13 +++ httemplate/graph/cust_bill_pkg.cgi | 2 + httemplate/search/cust_pkg_report.cgi | 22 ---- httemplate/search/report_cust_pkg.html | 47 ++++++++ httemplate/view/cust_main/contacts.html | 2 +- 15 files changed, 457 insertions(+), 104 deletions(-) create mode 100644 FS/FS/ConfDefaults.pm create mode 100644 httemplate/elements/select-cust-fields.html create mode 100644 httemplate/elements/select-cust_pkg-status.html create mode 100644 httemplate/elements/tr-select-cust-fields.html create mode 100644 httemplate/elements/tr-select-cust_pkg-status.html delete mode 100755 httemplate/search/cust_pkg_report.cgi create mode 100755 httemplate/search/report_cust_pkg.html diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index e7b9fa556..57c18e678 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -4,6 +4,7 @@ use vars qw($default_dir @config_items $DEBUG ); use IO::File; use File::Basename; use FS::ConfItem; +use FS::ConfDefaults; $DEBUG = 0; @@ -1619,18 +1620,9 @@ httemplate/docs/config.html { 'key' => 'cust-fields', 'section' => 'UI', - 'description' => 'Which customer fields to display on reports', + 'description' => 'Which customer fields to display on reports by default', 'type' => 'select', - 'select_enum' => [ - 'Customer: Last, First or Company (Last, First)', - 'Cust# | Customer: custnum | Last, First or Company (Last, First)', - 'Name | Company: Last, First | Company', - 'Cust# | Name | Company: custnum | Last, First | Company', - '(bill) Customer | (service) Customer: Last, First or Company (Last, First) | (same for service address if present)', - 'Cust# | (bill) Customer | (service) Customer: custnum | Last, First or Company (Last, First) | (same for service address if present)', - '(bill) Name | (bill) Company | (service) Name | (service) Company: Last, First | Company | (same for service address if present)', - 'Cust# | (bill) Name | (bill) Company | (service) Name | (service) Company: custnum | Last, First | Company | (same for service address if present)', - ], + 'select_hash' => [ FS::ConfDefaults->cust_fields_avail() ], }, { @@ -1709,6 +1701,21 @@ httemplate/docs/config.html 'type' => 'checkbox', }, + { + 'key' => 'batch-default_format', + 'section' => 'billing', + 'description' => 'Default format for batches.', + 'type' => 'select', + 'select_enum' => [ 'csv-td_canada_trust-merchant_pc_batch', 'BoM' ] + }, + + { + 'key' => 'batchconfig-BoM', + 'section' => 'billing', + 'description' => 'Configuration for Bank of Montreal batching, seven lines: 1. Origin ID, 2. Datacenter, 3. Typecode, 4. Short name, 5. Long name, 6. Bank, 7. Bank account', + 'type' => 'textarea', + }, + ); 1; diff --git a/FS/FS/ConfDefaults.pm b/FS/FS/ConfDefaults.pm new file mode 100644 index 000000000..b9cbcfbdf --- /dev/null +++ b/FS/FS/ConfDefaults.pm @@ -0,0 +1,68 @@ +package FS::ConfDefaults; + +=head1 NAME + +FS::ConfDefaults - Freeside configuration default and available values + +=head1 SYNOPSIS + + use FS::ConfDefaults; + + @avail_cust_fields = FS::ConfDefaults->cust_fields_avail(); + +=head1 DESCRIPTION + +Just a small class to keep config default and available values + +=head1 METHODS + +=over 4 + +=item cust_fields_avail + +Returns a list, suitable for assigning to a hash, of available values and +labels for customer fields values. + +=cut + +# XXX should use msgcat for "Day phone" and "Night phone", but how? +sub cust_fields_avail { ( + + 'Customer' => + 'Last, First or Company (Last, First)', + 'Cust# | Customer' => + 'custnum | Last, First or Company (Last, First)', + + 'Name | Company' => + 'Last, First | Company', + 'Cust# | Name | Company' => + 'custnum | Last, First | Company', + + '(bill) Customer | (service) Customer' => + 'Last, First or Company (Last, First) | (same for service contact if present)', + 'Cust# | (bill) Customer | (service) Customer' => + 'custnum | Last, First or Company (Last, First) | (same for service contact if present)', + + '(bill) Name | (bill) Company | (service) Name | (service) Company' => + 'Last, First | Company | (same for service address if present)', + 'Cust# | (bill) Name | (bill) Company | (service) Name | (service) Company' => + 'custnum | Last, First | Company | (same for service address if present)', + + 'Cust# | Name | Company | Address 1 | Address 2 | City | State | Zip | Country | Day phone | Night phone | Invoicing email(s)' => + 'custnum | Last, First | Company | (all address fields ) | Day phone | Night phone | Invoicing email(s)', + +); } + +=back + +=head1 BUGS + +Not yet. + +=head1 SEE ALSO + +L + +=cut + +1; diff --git a/FS/FS/UI/Web.pm b/FS/FS/UI/Web.pm index 10ddbf33f..080ac6e64 100644 --- a/FS/FS/UI/Web.pm +++ b/FS/FS/UI/Web.pm @@ -8,6 +8,8 @@ use FS::Record qw(dbdef); #use FS::UI #@ISA = qw( FS::UI ); +$DEBUG = 0; + use Date::Parse; sub parse_beginning_ending { my($cgi) = @_; @@ -31,73 +33,116 @@ sub parse_beginning_ending { } ### -# cust_main report methods +# cust_main report subroutines ### -=item cust_header -Returns an array of customer information headers according to the -B configuration setting. +=item cust_header [ CUST_FIELDS_VALUE ] + +Returns an array of customer information headers according to the supplied +customer fields value, or if no value is supplied, the B +configuration value. =cut use vars qw( @cust_fields ); -sub cust_sql_fields { - my @fields = qw( last first company ); - push @fields, map "ship_$_", @fields - if dbdef->table('cust_main')->column('ship_last'); - map "cust_main.$_", @fields; -} - sub cust_header { warn "FS::svc_Common::cust_header called" if $DEBUG; - my $conf = new FS::Conf; - my %header2method = ( - 'Customer' => 'name', - 'Cust#' => 'custnum', - 'Name' => 'contact', - 'Company' => 'company', - '(bill) Customer' => 'name', - '(service) Customer' => 'ship_name', - '(bill) Name' => 'contact', - '(service) Name' => 'ship_contact', - '(bill) Company' => 'company', - '(service) Company' => 'ship_company', + 'Customer' => 'name', + 'Cust#' => 'custnum', + 'Name' => 'contact', + 'Company' => 'company', + '(bill) Customer' => 'name', + '(service) Customer' => 'ship_name', + '(bill) Name' => 'contact', + '(service) Name' => 'ship_contact', + '(bill) Company' => 'company', + '(service) Company' => 'ship_company', + 'Address 1' => 'address1', + 'Address 2' => 'address2', + 'City' => 'city', + 'State' => 'state', + 'Zip' => 'zip', + 'Country' => 'country_full', + 'Day phone' => 'daytime', # XXX should use msgcat, but how? + 'Night phone' => 'night', # XXX should use msgcat, but how? + 'Invoicing email(s)' => 'invoicing_list_emailonly', ); + my $cust_fields; my @cust_header; - if ( $conf->exists('cust-fields') - && $conf->config('cust-fields') =~ /^([\w \|\#\(\)]+):/ - ) - { - warn " found cust-fields configuration value" - if $DEBUG; + if ( @_ && $_[0] ) { - my $cust_fields = $1; - @cust_header = split(/ \| /, $cust_fields); - @cust_fields = map { $header2method{$_} } @cust_header; - } else { - warn " no cust-fields configuration value found; using default 'Customer'" + warn " using supplied cust-fields override". + " (ignoring cust-fields config file)" if $DEBUG; - @cust_header = ( 'Customer' ); - @cust_fields = ( 'name' ); + $cust_fields = shift; + + } else { + + my $conf = new FS::Conf; + if ( $conf->exists('cust-fields') + && $conf->config('cust-fields') =~ /^([\w \|\#\(\)]+):?/ + ) + { + warn " found cust-fields configuration value" + if $DEBUG; + $cust_fields = $1; + } else { + warn " no cust-fields configuration value found; using default 'Customer'" + if $DEBUG; + $cust_fields = 'Customer'; + } + } + @cust_header = split(/ \| /, $cust_fields); + @cust_fields = map { $header2method{$_} } @cust_header; + #my $svc_x = shift; @cust_header; } -=item cust_fields +=item cust_sql_fields [ CUST_FIELDS_VALUE ] + +Returns a list of fields for the SELECT portion of an SQL query. + +As with L, the fields returned are +defined by the supplied customer fields setting, or if no customer fields +setting is supplied, the cust-fields configuration value. + +=cut + +sub cust_sql_fields { + + my @fields = qw( last first company ); + push @fields, map "ship_$_", @fields; + push @fields, 'country'; + + cust_header(@_); + #inefficientish, but tiny lists and only run once per page + push @fields, + grep { my $field = $_; grep { $_ eq $field } @cust_fields } + qw( address1 address2 city state zip daytime night ); + + map "cust_main.$_", @fields; +} + +=item cust_fields SVC_OBJECT [ CUST_FIELDS_VALUE ] Given a svc_ object that contains fields from cust_main (say, from a JOINed search. See httemplate/search/svc_* for examples), returns an array -of customer information according to the cust-fields configuration -setting, or "(unlinked)" if this service is not linked to a customer. +of customer information, or "(unlinked)" if this service is not linked to a +customer. + +As with L, the fields returned are +defined by the supplied customer fields setting, or if no customer fields +setting is supplied, the cust-fields configuration value. =cut @@ -107,7 +152,8 @@ sub cust_fields { "(cust_fields: @cust_fields)" if $DEBUG > 1; - cust_header() unless @cust_fields; + #cust_header(@_) unless @cust_fields; #now need to cache to keep cust_fields + # #override incase we were passed as a sub my $seen_unlinked = 0; map { diff --git a/FS/FS/cust_main_Mixin.pm b/FS/FS/cust_main_Mixin.pm index a114c5a8a..aa4143df1 100644 --- a/FS/FS/cust_main_Mixin.pm +++ b/FS/FS/cust_main_Mixin.pm @@ -89,6 +89,50 @@ sub ship_contact { : $self->cust_unlinked_msg; } +=item country_full + +Given an object that contains fields from cust_main (say, from a JOINed +search; see httemplate/search/ for examples), returns the equivalent of the +FS::cust_main I method, or "(unlinked)" if this object is not +linked to a customer. + +=cut + +sub country_full { + my $self = shift; + $self->cust_linked + ? FS::cust_main::country_full($self) + : $self->cust_unlinked_msg; +} + +=item invoicing_list_emailonly + +Given an object that contains fields from cust_main (say, from a JOINed +search; see httemplate/search/ for examples), returns the equivalent of the +FS::cust_main I method, or "(unlinked)" if this object is not +linked to a customer. + +=cut + +sub invoicing_list_emailonly { + my $self = shift; + warn "invoicing_list_email only called on $self, ". + "custnum ". $self->custnum. "\n"; + $self->cust_linked + ? FS::cust_main::invoicing_list_emailonly($self) + : $self->cust_unlinked_msg; +} + +#read-only +sub invoicing_list { + my $self = shift; + $self->cust_linked + ? FS::cust_main::invoicing_list($self) + : (); +} + +=cut + =back =head1 BUGS diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 783cc73a3..ed9f2cbc6 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -2,6 +2,7 @@ package FS::cust_pkg; use strict; use vars qw(@ISA $disable_agentcheck @SVCDB_CANCEL_SEQ $DEBUG); +use Tie::IxHash; use FS::UID qw( getotaker dbh ); use FS::Misc qw( send_email ); use FS::Record qw( qsearch qsearchs ); @@ -824,26 +825,45 @@ Returns a short status string for this package, currently: sub status { my $self = shift; + my $freq = length($self->freq) ? $self->freq : $self->part_pkg->freq; + return 'cancelled' if $self->get('cancel'); return 'suspended' if $self->susp; return 'not yet billed' unless $self->setup; - return 'one-time charge' if $self->part_pkg->freq =~ /^(0|$)/; + return 'one-time charge' if $freq =~ /^(0|$)/; return 'active'; } -=item statuscolor +=item statuses -Returns a hex triplet color string for this package's status. +Class method that returns the list of possible status strings for pacakges +(see L). For example: + + @statuses = FS::cust_pkg->statuses(); =cut -my %statuscolor = ( +tie my %statuscolor, 'Tie::IxHash', 'not yet billed' => '000000', 'one-time charge' => '000000', 'active' => '00CC00', 'suspended' => 'FF9900', 'cancelled' => 'FF0000', -); +; + +sub statuses { + my $self = shift; #could be class... + grep { $_ !~ /^(not yet billed)$/ } #this is a dumb status anyway + # mayble split btw one-time vs. recur + keys %statuscolor; +} + +=item statuscolor + +Returns a hex triplet color string for this package's status. + +=cut + sub statuscolor { my $self = shift; $statuscolor{$self->status}; @@ -1163,7 +1183,7 @@ sub reexport { =back -=head1 CLASS METHOD +=head1 CLASS METHODS =over 4 @@ -1178,6 +1198,17 @@ sub recurring_sql { " where cust_pkg.pkgpart = part_pkg.pkgpart ) "; } +=item onetime_sql + +Returns an SQL expression identifying one-time packages. + +=cut + +sub onetime_sql { " + '0' = ( select freq from part_pkg + where cust_pkg.pkgpart = part_pkg.pkgpart ) +"; } + =item active_sql Returns an SQL expression identifying active packages. @@ -1190,6 +1221,19 @@ sub active_sql { " AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 ) "; } +=item inactive_sql + +Returns an SQL expression identifying inactive packages (one-time packages +that are otherwise unsuspended/uncancelled). + +=cut + +sub inactive_sql { " + ". $_[0]->onetime_sql(). " + AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 ) + AND ( cust_pkg.susp IS NULL OR cust_pkg.susp = 0 ) +"; } + =item susp_sql =item suspended_sql @@ -1198,11 +1242,13 @@ Returns an SQL expression identifying suspended packages. =cut sub suspended_sql { susp_sql(@_); } -sub susp_sql { " - ". $_[0]->recurring_sql(). " - AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 ) - AND cust_pkg.susp IS NOT NULL AND cust_pkg.susp != 0 -"; } +sub susp_sql { + #$_[0]->recurring_sql(). ' AND '. + " + ( cust_pkg.cancel IS NULL OR cust_pkg.cancel = 0 ) + AND cust_pkg.susp IS NOT NULL AND cust_pkg.susp != 0 + "; +} =item cancel_sql =item cancelled_sql @@ -1212,10 +1258,10 @@ Returns an SQL exprression identifying cancelled packages. =cut sub cancelled_sql { cancel_sql(@_); } -sub cancel_sql { " - ". $_[0]->recurring_sql(). " - AND cust_pkg.cancel IS NOT NULL AND cust_pkg.cancel != 0 -"; } +sub cancel_sql { + #$_[0]->recurring_sql(). ' AND '. + "cust_pkg.cancel IS NOT NULL AND cust_pkg.cancel != 0"; +} =head1 SUBROUTINES diff --git a/httemplate/config/config.cgi b/httemplate/config/config.cgi index 21f79a9dc..6008c0e42 100644 --- a/httemplate/config/config.cgi +++ b/httemplate/config/config.cgi @@ -55,25 +55,65 @@ function SafeOnsubmit() { #warn $i->key unless defined($type); %> <% if ( $type eq '' ) { %> - no type + + no type + <% } elsif ( $type eq 'textarea' ) { %> - + + + <% } elsif ( $type eq 'checkbox' ) { %> - exists($i->key) ? ' CHECKED' : '' %>> + + exists($i->key) ? ' CHECKED' : '' %>> + <% } elsif ( $type eq 'text' ) { %> - + + + <% } elsif ( $type eq 'select' || $type eq 'selectmultiple' ) { %> - > + <% + my %hash = (); + if ( $i->select_enum ) { + tie %hash, 'Tie::IxHash', + '' => '', map { $_ => $_ } @{ $i->select_enum }; + } elsif ( $i->select_hash ) { + if ( ref($i->select_hash) eq 'ARRAY' ) { + tie %hash, 'Tie::IxHash', + '' => '', @{ $i->select_hash }; + } else { + tie %hash, 'Tie::IxHash', + '' => '', %{ $i->select_hash }; + } + } else { + %hash = ( '' => 'WARNING: neither select_enum nor select_hash specified in Conf.pm for configuration option "'. $i->key. '"' ); + } + + my %saw = (); + foreach my $value ( keys %hash ) { + local($^W)=0; next if $saw{$value}++; + my $label = $hash{$value}; + %> + +