From: ivan Date: Wed, 29 Dec 2004 12:00:08 +0000 (+0000) Subject: historical (immutable) invoice details about services and other history infrastructure X-Git-Tag: BEFORE_FINAL_MASONIZE~751 X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=commitdiff_plain;h=f7afca1829f8496509d10806439c37fcc1349135 historical (immutable) invoice details about services and other history infrastructure --- diff --git a/ANNOUNCE.1.5.0 b/ANNOUNCE.1.5.0 index 9d2b97ee6..fad6c92fe 100644 --- a/ANNOUNCE.1.5.0 +++ b/ANNOUNCE.1.5.0 @@ -21,7 +21,7 @@ 1.5.0pre7: - fix bug that could cause mis-billing on upgrades! (new installs ok) - update install documentation for 1.5 HTML::Mason or Apache::ASP install -# - historical late notice viewing in web interface +- historical late notice viewing in web interface - VoIP billing for CDRs from RADIUS - promotional codes for signup - lots of RT integration, integrated RT upgraded to 3.2.2 diff --git a/FS/FS/Record.pm b/FS/FS/Record.pm index 5a6bb579e..cf0ac3b2f 100644 --- a/FS/FS/Record.pm +++ b/FS/FS/Record.pm @@ -180,7 +180,7 @@ sub create { } } -=item qsearch TABLE, HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ +=item qsearch TABLE, HASHREF, SELECT, EXTRA_SQL, CACHE_OBJ, AS Searches the database for all records matching (at least) the key/value pairs in HASHREF. Returns all the records found as `FS::TABLE' objects if that @@ -199,7 +199,7 @@ objects. =cut sub qsearch { - my($stable, $record, $select, $extra_sql, $cache ) = @_; + my($stable, $record, $select, $extra_sql, $cache, $as ) = @_; #$stable =~ /^([\w\_]+)$/ or die "Illegal table: $table"; #for jsearch $stable =~ /^([\w\s\(\)\.\,\=]+)$/ or die "Illegal table: $stable"; @@ -223,6 +223,7 @@ sub qsearch { } my $statement = "SELECT $select FROM $stable"; + $statement .= " AS $as" if $as; if ( @real_fields or @virtual_fields ) { $statement .= ' WHERE '. join(' AND ', ( map { diff --git a/FS/FS/cust_bill.pm b/FS/FS/cust_bill.pm index 0306c010c..93c179a2a 100644 --- a/FS/FS/cust_bill.pm +++ b/FS/FS/cust_bill.pm @@ -356,14 +356,13 @@ sub send { 'body' => \@print_text, ); die "can't email invoice: $error\n" if $error; + #die "$error\n" if $error; } - if ( $conf->config('invoice_latex') ) { - @print_text = $self->print_ps('', $template); - } - if ( grep { $_ eq 'POST' } @invoicing_list ) { #postal + @print_text = $self->print_ps('', $template) + if $conf->config('invoice_latex'); my $lpr = $conf->config('lpr'); open(LPR, "|$lpr") or die "Can't open pipe to $lpr: $!\n"; @@ -796,7 +795,8 @@ sub print_text { push @buf, [ $description, $money_char. sprintf("%10.2f", $cust_bill_pkg->setup) ]; push @buf, - map { [ " ". $_->[0]. ": ". $_->[1], '' ] } $cust_pkg->labels; + map { [ " ". $_->[0]. ": ". $_->[1], '' ] } + $cust_pkg->h_labels($self->_date); } if ( $cust_bill_pkg->recur != 0 ) { @@ -806,7 +806,8 @@ sub print_text { $money_char. sprintf("%10.2f", $cust_bill_pkg->recur) ]; push @buf, - map { [ " ". $_->[0]. ": ". $_->[1], '' ] } $cust_pkg->labels; + map { [ " ". $_->[0]. ": ". $_->[1], '' ] } + $cust_pkg->h_labels($cust_bill_pkg->edate, $cust_bill_pkg->sdate); } push @buf, map { [ " $_", '' ] } $cust_bill_pkg->details; @@ -1366,45 +1367,32 @@ sub _items_cust_bill_pkg { my $part_pkg = qsearchs('part_pkg', { pkgpart=>$cust_pkg->pkgpart } ); my $pkg = $part_pkg->pkg; - my %labels; - #tie %labels, 'Tie::IxHash'; - push @{ $labels{$_->[0]} }, $_->[1] foreach $cust_pkg->labels; - my @ext_description; - foreach my $label ( keys %labels ) { - my @values = @{ $labels{$label} }; - my $num = scalar(@values); - if ( $num > 5 ) { - push @ext_description, "$label ($num)"; - } else { - push @ext_description, map { "$label: $_" } @values; - } - } - if ( $cust_bill_pkg->setup != 0 ) { my $description = $pkg; $description .= ' Setup' if $cust_bill_pkg->recur != 0; - my @d = @ext_description; + my @d = $cust_pkg->h_labels_short($self->_date); push @d, $cust_bill_pkg->details if $cust_bill_pkg->recur == 0; push @b, { - 'description' => $description, - #'pkgpart' => $part_pkg->pkgpart, - 'pkgnum' => $cust_pkg->pkgnum, - 'amount' => sprintf("%10.2f", $cust_bill_pkg->setup), - 'ext_description' => \@d, + description => $description, + #pkgpart => $part_pkg->pkgpart, + pkgnum => $cust_pkg->pkgnum, + amount => sprintf("%10.2f", $cust_bill_pkg->setup), + ext_description => \@d, }; } if ( $cust_bill_pkg->recur != 0 ) { push @b, { - 'description' => "$pkg (" . + description => "$pkg (" . time2str('%x', $cust_bill_pkg->sdate). ' - '. time2str('%x', $cust_bill_pkg->edate). ')', - #'pkgpart' => $part_pkg->pkgpart, - 'pkgnum' => $cust_pkg->pkgnum, - 'amount' => sprintf("%10.2f", $cust_bill_pkg->recur), - 'ext_description' => [ @ext_description, - $cust_bill_pkg->details, - ], + #pkgpart => $part_pkg->pkgpart, + pkgnum => $cust_pkg->pkgnum, + amount => sprintf("%10.2f", $cust_bill_pkg->recur), + ext_description => [ $cust_pkg->h_labels_short($cust_bill_pkg->edate, + $cust_bill_pkg->sdate), + $cust_bill_pkg->details, + ], }; } diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm index c42d22239..44fb8d696 100644 --- a/FS/FS/cust_main.pm +++ b/FS/FS/cust_main.pm @@ -1009,6 +1009,27 @@ sub unsuspended_pkgs { grep { ! $_->susp } $self->ncancelled_pkgs; } +=item num_cancelled_pkgs + +Returns the number of cancelled packages (see L) for this +customer. + +=cut + +sub num_cancelled_pkgs { + my $self = shift; + $self->num_pkgs("cancel IS NOT NULL AND cust_pkg.cancel != 0"); +} + +sub num_pkgs { + my( $self, $sql ) = @_; + my $sth = dbh->prepare( + "SELECT COUNT(*) FROM cust_pkg WHERE custnum = ? AND $sql" + ) or die dbh->errstr; + $sth->execute($self->custnum) or die $sth->errstr; + $sth->fetchrow_arrayref->[0]; +} + =item unsuspend Unsuspends all unflagged suspended packages (see L diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm index 630e88ea7..ced142398 100644 --- a/FS/FS/cust_pkg.pm +++ b/FS/FS/cust_pkg.pm @@ -11,6 +11,7 @@ use FS::cust_main; use FS::type_pkgs; use FS::pkg_svc; use FS::cust_bill_pkg; +use FS::h_cust_svc; # need to 'use' these instead of 'require' in sub { cancel, suspend, unsuspend, # setup } @@ -543,21 +544,50 @@ sub cust_svc { #if ( $self->{'_svcnum'} ) { # values %{ $self->{'_svcnum'}->cache }; #} else { - map { $_->[0] } - sort { $b->[1] cmp $a->[1] or $a->[2] <=> $b->[2] } - map { - my $pkg_svc = qsearchs( 'pkg_svc', { 'pkgpart' => $self->pkgpart, - 'svcpart' => $_->svcpart } ); - [ $_, - $pkg_svc ? $pkg_svc->primary_svc : '', - $pkg_svc ? $pkg_svc->quantity : 0, - ]; - } - qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } ); + $self->_sort_cust_svc( + [ qsearch( 'cust_svc', { 'pkgnum' => $self->pkgnum } ) ] + ); #} } +=item h_cust_svc END_TIMESTAMP [ START_TIMESTAMP ] + +Returns historical services for this package created before END TIMESTAMP and +(optionally) not cancelled before START_TIMESTAMP, as FS::h_cust_svc objects +(see L). + +=cut + +sub h_cust_svc { + my $self = shift; + + $self->_sort_cust_svc( + [ qsearch( 'h_cust_svc', + { 'pkgnum' => $self->pkgnum, }, + FS::h_cust_svc->sql_h_search(@_), + ) + ] + ); +} + +sub _sort_cust_svc { + my( $self, $arrayref ) = @_; + + map { $_->[0] } + sort { $b->[1] cmp $a->[1] or $a->[2] <=> $b->[2] } + map { + my $pkg_svc = qsearchs( 'pkg_svc', { 'pkgpart' => $self->pkgpart, + 'svcpart' => $_->svcpart } ); + [ $_, + $pkg_svc ? $pkg_svc->primary_svc : '', + $pkg_svc ? $pkg_svc->quantity : 0, + ]; + } + @$arrayref; + +} + =item num_cust_svc [ SVCPART ] Returns the number of provisioned services for this package. If a svcpart is @@ -606,6 +636,52 @@ sub labels { map { [ $_->label ] } $self->cust_svc; } +=item h_labels END_TIMESTAMP [ START_TIMESTAMP ] + +Like the labels method, but returns historical information on services that +were active as of END_TIMESTAMP and (optionally) not cancelled before +START_TIMESTAMP. + +Returns a list of lists, calling the label method for all (historical) services +(see L) of this billing item. + +=cut + +sub h_labels { + my $self = shift; + map { [ $_->label(@_) ] } $self->h_cust_svc(@_); +} + +=item h_labels_short END_TIMESTAMP [ START_TIMESTAMP ] + +Like h_labels, except returns a simple flat list, and shortens long +(currently >5) lists of identical services to one line that lists the service +label and the number of individual services rather than individual items. + +=cut + +sub h_labels_short { + my $self = shift; + + my %labels; + #tie %labels, 'Tie::IxHash'; + push @{ $labels{$_->[0]} }, $_->[1] + foreach $self->h_labels(@_); + my @labels; + foreach my $label ( keys %labels ) { + my @values = @{ $labels{$label} }; + my $num = scalar(@values); + if ( $num > 5 ) { + push @labels, "$label ($num)"; + } else { + push @labels, map { "$label: $_" } @values; + } + } + + @labels; + +} + =item cust_main Returns the parent customer object (see L). diff --git a/FS/FS/cust_svc.pm b/FS/FS/cust_svc.pm index 8990d54c1..9cb7a8163 100644 --- a/FS/FS/cust_svc.pm +++ b/FS/FS/cust_svc.pm @@ -1,8 +1,8 @@ package FS::cust_svc; use strict; -use vars qw( @ISA $ignore_quantity ); -use Carp qw( cluck ); +use vars qw( @ISA $DEBUG $ignore_quantity ); +use Carp qw( carp cluck ); use FS::Conf; use FS::Record qw( qsearch qsearchs dbh ); use FS::cust_pkg; @@ -19,6 +19,8 @@ use FS::part_export; @ISA = qw( FS::Record ); +$DEBUG = 1; + $ignore_quantity = 0; sub _cache { @@ -276,37 +278,44 @@ Returns a list consisting of: sub label { my $self = shift; - my $svcdb = $self->part_svc->svcdb; + carp "FS::cust_svc::label called on $self" if $DEBUG; my $svc_x = $self->svc_x - or die "can't find $svcdb.svcnum ". $self->svcnum; + or die "can't find ". $self->part_svc->svcdb. '.svcnum '. $self->svcnum; + $self->_svc_label($svc_x); +} + +sub _svc_label { + my( $self, $svc_x ) = ( shift, shift ); + my $svcdb = $self->part_svc->svcdb; + my $tag; if ( $svcdb eq 'svc_acct' ) { - $tag = $svc_x->email; + $tag = $svc_x->email(@_); } elsif ( $svcdb eq 'svc_forward' ) { if ( $svc_x->srcsvc ) { - my $svc_acct = $svc_x->srcsvc_acct; - $tag = $svc_acct->email; + my $svc_acct = $svc_x->srcsvc_acct(@_); + $tag = $svc_acct->email(@_); } else { $tag = $svc_x->src; } $tag .= '->'; if ( $svc_x->dstsvc ) { - my $svc_acct = $svc_x->dstsvc_acct; - $tag .= $svc_acct->email; + my $svc_acct = $svc_x->dstsvc_acct(@_); + $tag .= $svc_acct->email(@_); } else { $tag .= $svc_x->dst; } } elsif ( $svcdb eq 'svc_domain' ) { $tag = $svc_x->getfield('domain'); } elsif ( $svcdb eq 'svc_www' ) { - my $domain = qsearchs( 'domain_record', { 'recnum' => $svc_x->recnum } ); - $tag = $domain->zone; + my $domain_record = $svc_x->domain_record; + $tag = $domain_record->zone; } elsif ( $svcdb eq 'svc_broadband' ) { $tag = $svc_x->ip_addr; } elsif ( $svcdb eq 'svc_external' ) { my $conf = new FS::Conf; if ( $conf->config('svc_external-display_type') eq 'artera_turbo' ) { - $tag = sprintf('%010d', $svc_x->id). '-'. $svc_x->title; + $tag = sprintf('%010d', $svc_x->id). '-'. sprintf('%010d', $svc_x->title); } else { $tag = $svc_x->id. ': '. $svc_x->title; } @@ -314,7 +323,9 @@ sub label { cluck "warning: asked for label of unsupported svcdb; using svcnum"; $tag = $svc_x->getfield('svcnum'); } + $self->part_svc->svc, $tag, $svcdb; + } =item svc_x @@ -566,7 +577,7 @@ sub get_session_history { my @sessions = (); foreach my $part_export ( @part_export ) { - push @sessions, $part_export->usage_sessions( $self->svc_x, $start, $end ); + push @sessions, $part_export->usage_sessions( $start, $end, $self->svc_x ); } \@sessions; diff --git a/FS/FS/h_Common.pm b/FS/FS/h_Common.pm new file mode 100644 index 000000000..58ead88e4 --- /dev/null +++ b/FS/FS/h_Common.pm @@ -0,0 +1,83 @@ +package FS::h_Common; + +use strict; +use FS::Record qw(dbdef); + +=head1 NAME + +FS::h_Common - History table "mixin" common base class + +=head1 SYNOPSIS + +package FS::h_tablename; +@ISA = qw( FS::h_Common FS::tablename ); + +sub table { 'h_table_name'; } + +sub insert { return "can't insert history records manually"; } +sub delete { return "can't delete history records"; } +sub replace { return "can't modify history records"; } + +=head1 DESCRIPTION + +FS::h_Common is intended as a "mixin" base class for history table classes to +inherit from. + +=head1 METHODS + +=over 4 + +=item sql_h_search END_TIMESTAMP [ START_TIMESTAMP ] + +Returns an a list consisting of the "SELECT" and "EXTRA_SQL" SQL fragments to +search for the appropriate history records created before END_TIMESTAMP +and (optionally) not cancelled before START_TIMESTAMP. + +=cut + +sub sql_h_search { + my( $self, $end ) = ( shift, shift ); + + my $table = $self->table; + my $pkey = dbdef->table($table)->primary_key + or die "can't (yet) search history table $table without a primary key"; + + my $notcancelled = ''; + if ( scalar(@_) && $_[0] ) { + $notcancelled = "AND 0 = ( SELECT COUNT(*) FROM $table as notdel + WHERE notdel.$pkey = maintable.$pkey + AND notdel.history_action = 'delete' + AND notdel.history_date > maintable.history_date + AND notdel.history_date <= $_[0] + )"; + } + + ( + "DISTINCT ON ( $pkey ) *", + + "AND history_date <= $end + AND ( history_action = 'insert' + OR history_action = 'replace_new' + ) + $notcancelled + ORDER BY $pkey ASC, history_date DESC", + + '', + + 'maintable', + ); + +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L, schema.html from the base documentation + +=cut + +1; + diff --git a/FS/FS/h_cust_svc.pm b/FS/FS/h_cust_svc.pm new file mode 100644 index 000000000..9ef60fdc2 --- /dev/null +++ b/FS/FS/h_cust_svc.pm @@ -0,0 +1,84 @@ +package FS::h_cust_svc; + +use strict; +use vars qw( @ISA $DEBUG ); +use Carp; +use FS::Record qw(qsearchs); +use FS::h_Common; +use FS::cust_svc; + +@ISA = qw( FS::h_Common FS::cust_svc ); + +$DEBUG = 0; + +sub table { 'h_cust_svc'; } + +=head1 NAME + +FS::h_cust_svc - Object method for h_cust_svc objects + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +An FS::h_cust_svc object represents a historical service. FS::h_cust_svc +inherits from FS::h_Common and FS::cust_svc. + +=head1 METHODS + +=over 4 + +=item label END_TIMESTAMP [ START_TIMESTAMP ] + +Returns a list consisting of: +- The name of this historical service (from part_svc) +- A meaningful identifier (username, domain, or mail alias) +- The table name (i.e. svc_domain) for this historical service + +=cut + +sub label { + my $self = shift; + carp "FS::h_cust_svc::label called on $self" if $DEBUG; + my $svc_x = $self->h_svc_x(@_) + or die "can't find h_". $self->part_svc->svcdb. '.svcnum '. $self->svcnum; + $self->_svc_label($svc_x, @_); +} + +=item h_svc_x END_TIMESTAMP [ START_TIMESTAMP ] + +Returns the FS::h_svc_XXX object for this service as of END_TIMESTAMP (i.e. an +FS::h_svc_acct object or FS::h_svc_domain object, etc.) and (optionally) not +cancelled before START_TIMESTAMP. + +=cut + +#false laziness w/cust_pkg::h_cust_svc +sub h_svc_x { + my $self = shift; + my $svcdb = $self->part_svc->svcdb; + #if ( $svcdb eq 'svc_acct' && $self->{'_svc_acct'} ) { + # $self->{'_svc_acct'}; + #} else { + warn "requiring FS/h_$svcdb.pm" if $DEBUG; + require "FS/h_$svcdb.pm"; + qsearchs( "h_$svcdb", + { 'svcnum' => $self->svcnum, }, + "FS::h_$svcdb"->sql_h_search(@_), + ); + #} +} + +=back + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=cut + +1; + diff --git a/FS/FS/h_svc_acct.pm b/FS/FS/h_svc_acct.pm new file mode 100644 index 000000000..cd98dd40e --- /dev/null +++ b/FS/FS/h_svc_acct.pm @@ -0,0 +1,53 @@ +package FS::h_svc_acct; + +use strict; +use vars qw( @ISA ); +use FS::Record qw(qsearchs); +use FS::h_Common; +use FS::svc_acct; +use FS::h_svc_domain; + +@ISA = qw( FS::h_Common FS::svc_acct ); + +sub table { 'h_svc_acct' }; + +=head1 NAME + +FS::h_svc_acct - Historical account objects + +=head1 SYNOPSIS + +=head1 METHODS + +=over 4 + +=item svc_domain + +=cut + +sub svc_domain { + my $self = shift; + qsearchs( 'h_svc_domain', + { 'svcnum' => $self->domsvc }, + FS::h_svc_domain->sql_h_search(@_), + ); +} + +=back + +=head1 DESCRIPTION + +An FS::h_svc_acct object represents a historical account. FS::h_svc_acct +inherits from FS::h_Common and FS::svc_acct. + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=cut + +1; + diff --git a/FS/FS/h_svc_broadband.pm b/FS/FS/h_svc_broadband.pm new file mode 100644 index 000000000..d6038fbe8 --- /dev/null +++ b/FS/FS/h_svc_broadband.pm @@ -0,0 +1,33 @@ +package FS::h_svc_broadband; + +use strict; +use vars qw( @ISA ); +use FS::h_Common; +use FS::svc_broadband; + +@ISA = qw( FS::h_Common FS::svc_broadband ); + +sub table { 'h_svc_broadband' }; + +=head1 NAME + +FS::h_svc_broadband - Historical broadband connection objects + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +An FS::h_svc_broadband object represents a historical broadband connection. +FS::h_svc_broadband inherits from FS::h_Common and FS::svc_broadband. + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=cut + +1; + diff --git a/FS/FS/h_svc_domain.pm b/FS/FS/h_svc_domain.pm new file mode 100644 index 000000000..60d54f7d1 --- /dev/null +++ b/FS/FS/h_svc_domain.pm @@ -0,0 +1,33 @@ +package FS::h_svc_domain; + +use strict; +use vars qw( @ISA ); +use FS::h_Common; +use FS::svc_domain; + +@ISA = qw( FS::h_Common FS::svc_domain ); + +sub table { 'h_svc_domain' }; + +=head1 NAME + +FS::h_svc_domain - Historical domain objects + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +An FS::h_svc_domain object represents a historical domain. FS::h_svc_domain +inherits from FS::h_Common and FS::svc_domain. + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=cut + +1; + diff --git a/FS/FS/h_svc_external.pm b/FS/FS/h_svc_external.pm new file mode 100644 index 000000000..5eb706410 --- /dev/null +++ b/FS/FS/h_svc_external.pm @@ -0,0 +1,33 @@ +package FS::h_svc_external; + +use strict; +use vars qw( @ISA ); +use FS::h_Common; +use FS::svc_external; + +@ISA = qw( FS::h_Common FS::svc_external ); + +sub table { 'h_svc_external' }; + +=head1 NAME + +FS::h_svc_external - Historical externally tracked service objects + +=head1 SYNOPSIS + +=head1 DESCRIPTION + +An FS::h_svc_external object represents a historical externally tracked service. +FS::h_svc_external inherits from FS::h_Common and FS::svc_external. + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=cut + +1; + diff --git a/FS/FS/h_svc_forward.pm b/FS/FS/h_svc_forward.pm new file mode 100644 index 000000000..231f9dfae --- /dev/null +++ b/FS/FS/h_svc_forward.pm @@ -0,0 +1,65 @@ +package FS::h_svc_forward; + +use strict; +use vars qw( @ISA ); +se FS::Record qw(qsearchs); +use FS::h_Common; +use FS::svc_forward; +use FS::h_svc_acct; + +@ISA = qw( FS::h_Common FS::svc_forward ); + +sub table { 'h_svc_forward' }; + +=head1 NAME + +FS::h_svc_forward - Historical mail forwarding alias objects + +=head1 SYNOPSIS + +=head1 METHODS + +=over 4 + +=item srcsvc_acct + +=cut + +sub srcsvc_acct { + my $self = shift; + qsearchs( 'h_svc_acct', + { 'svcnum' => $self->srcsvc }, + FS::h_svc_acct->sql_h_search(@_), + ); +} + +=item dstsvc_acct + +=cut + +sub dstsvc_acct { + my $self = shift; + qsearchs( 'h_svc_acct', + { 'svcnum' => $self->dstsvc }, + FS::h_svc_acct->sql_h_search(@_), + ); +} + +=back + +=head1 DESCRIPTION + +An FS::h_svc_forward object represents a historical mail forwarding alias. +FS::h_svc_forward inherits from FS::h_Common and FS::svc_forward. + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=cut + +1; + diff --git a/FS/FS/h_svc_www.pm b/FS/FS/h_svc_www.pm new file mode 100644 index 000000000..f2f8af820 --- /dev/null +++ b/FS/FS/h_svc_www.pm @@ -0,0 +1,53 @@ +package FS::h_svc_www; + +use strict; +use vars qw( @ISA ); +se FS::Record qw(qsearchs); +use FS::h_Common; +use FS::svc_www; +use FS::h_domain_record; + +@ISA = qw( FS::h_Common FS::svc_www ); + +sub table { 'h_svc_www' }; + +=head1 NAME + +FS::h_svc_www - Historical web virtual host objects + +=head1 SYNOPSIS + +=head1 METHODS + +=over 4 + +=item domain_record + +=cut + +sub domain_record { + my $self = shift; + qsearchs( 'h_domain_record', + { 'recnum' => $self->recnum }, + FS::h_domain_record->sql_h_search(@_), + ); +} + +=back + +=head1 DESCRIPTION + +An FS::h_svc_www object represents a historical web virtual host. +FS::h_svc_www inherits from FS::h_Common and FS::svc_www. + +=head1 BUGS + +=head1 SEE ALSO + +L, L, L, schema.html from the base +documentation. + +=cut + +1; + diff --git a/FS/FS/svc_acct.pm b/FS/FS/svc_acct.pm index ec0e1d506..8e47abfc5 100644 --- a/FS/FS/svc_acct.pm +++ b/FS/FS/svc_acct.pm @@ -1028,7 +1028,7 @@ Returns the domain associated with this account. sub domain { my $self = shift; die "svc_acct.domsvc is null for svcnum ". $self->svcnum unless $self->domsvc; - my $svc_domain = $self->svc_domain + my $svc_domain = $self->svc_domain(@_) or die "no svc_domain.svcnum for svc_acct.domsvc ". $self->domsvc; $svc_domain->domain; } @@ -1066,7 +1066,7 @@ Returns an email address associated with the account. sub email { my $self = shift; - $self->username. '@'. $self->domain; + $self->username. '@'. $self->domain(@_); } =item acct_snarf diff --git a/FS/MANIFEST b/FS/MANIFEST index dabc08c70..32785c20e 100644 --- a/FS/MANIFEST +++ b/FS/MANIFEST @@ -64,6 +64,14 @@ FS/cust_pkg.pm FS/cust_refund.pm FS/cust_credit_refund.pm FS/cust_svc.pm +FS/h_Common.pm +FS/h_cust_svc.pm +FS/h_svc_acct.pm +FS/h_svc_broadband.pm +FS/h_svc_domain.pm +FS/h_svc_external.pm +FS/h_svc_forward.pm +FS/h_svc_www.pm FS/part_bill_event.pm FS/export_svc.pm FS/part_export.pm @@ -171,6 +179,15 @@ t/cust_pay_refund.t t/cust_pkg.t t/cust_refund.t t/cust_svc.t +t/h_cust_svc.t +t/h_Common.t +t/h_cust_svc.t +t/h_svc_acct.t +t/h_svc_broadband.t +t/h_svc_domain.t +t/h_svc_external.t +t/h_svc_forward.t +t/h_svc_www.t t/cust_tax_exempt.t t/domain_record.t t/nas.t diff --git a/FS/t/h_Common.t b/FS/t/h_Common.t new file mode 100644 index 000000000..174bb99e6 --- /dev/null +++ b/FS/t/h_Common.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_Common; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/h_cust_svc.t b/FS/t/h_cust_svc.t new file mode 100644 index 000000000..a7dabbea0 --- /dev/null +++ b/FS/t/h_cust_svc.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_cust_svc; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/h_svc_acct.t b/FS/t/h_svc_acct.t new file mode 100644 index 000000000..9c94d0894 --- /dev/null +++ b/FS/t/h_svc_acct.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_svc_acct; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/h_svc_broadband.t b/FS/t/h_svc_broadband.t new file mode 100644 index 000000000..b8e5c7c82 --- /dev/null +++ b/FS/t/h_svc_broadband.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_svc_broadband; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/h_svc_domain.t b/FS/t/h_svc_domain.t new file mode 100644 index 000000000..87d2a09bd --- /dev/null +++ b/FS/t/h_svc_domain.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_svc_domain; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/h_svc_external.t b/FS/t/h_svc_external.t new file mode 100644 index 000000000..5248f876d --- /dev/null +++ b/FS/t/h_svc_external.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_svc_external; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/h_svc_forward.t b/FS/t/h_svc_forward.t new file mode 100644 index 000000000..64731d562 --- /dev/null +++ b/FS/t/h_svc_forward.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_svc_forward; +$loaded=1; +print "ok 1\n"; diff --git a/FS/t/h_svc_www.t b/FS/t/h_svc_www.t new file mode 100644 index 000000000..07558ce65 --- /dev/null +++ b/FS/t/h_svc_www.t @@ -0,0 +1,5 @@ +BEGIN { $| = 1; print "1..1\n" } +END {print "not ok 1\n" unless $loaded;} +use FS::h_svc_www; +$loaded=1; +print "ok 1\n"; diff --git a/httemplate/view/cust_main.cgi b/httemplate/view/cust_main.cgi index d5d77f2a5..8794f3074 100755 --- a/httemplate/view/cust_main.cgi +++ b/httemplate/view/cust_main.cgi @@ -109,13 +109,6 @@ Comments <% } %>

-<%= include('cust_main/order_pkg.html', $cust_main ) %> - -<% if ( $conf->config('payby-default') ne 'HIDE' ) { %> - <%= include('cust_main/quick-charge.html', $cust_main ) %> -
-<% } %> - <%= include('cust_main/packages.html', $cust_main ) %> <% if ( $conf->config('payby-default') ne 'HIDE' ) { %> diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index c5a0706d6..068a8276f 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -5,8 +5,38 @@ my $packages = get_packages($cust_main, $conf); %> -Packages -( Bulk order and cancel packages (preserves services) ) +Packages + +<%= include('order_pkg.html', $cust_main ) %> + +<% if ( $conf->config('payby-default') ne 'HIDE' ) { %> + <%= include('quick-charge.html', $cust_main ) %> +<% } %> + +Bulk order and cancel packages (preserves services) +

+ +<% if ( @$packages ) { %> +Current packages +<% } %> + +<% if ( $cust_main->num_cancelled_pkgs ) { + if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me + || ( $conf->exists('hidecancelledpackages') + && ! $cgi->param('showcancelledpackages') + ) + ) + { + $cgi->param('showcancelledpackages', 1); +%> + ( show +<% } else { + $cgi->param('showcancelledpackages', 0); +%> + ( hide +<% } %> + cancelled packages ) +<% } %> <% if ( @$packages ) { %> @@ -159,12 +189,14 @@ foreach my $pkg (sort pkgsort_pkgnum_cancel @$packages) { } } } -print ''; -} - #end display packages %> + +<% } else { %> +
+<% } %> + <% #subroutines @@ -173,12 +205,18 @@ sub get_packages { my $conf = shift; my @packages = (); + my $method; + if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me + || ( $conf->exists('hidecancelledpackages') + && ! $cgi->param('showcancelledpackages') ) + ) + { + $method = 'ncancelled_pkgs'; + } else { + $method = 'all_pkgs'; + } - foreach my $cust_pkg ( - $conf->exists('hidecancelledpackages') - ? $cust_main->ncancelled_pkgs - : $cust_main->all_pkgs - ) { + foreach my $cust_pkg ( $cust_main->$method() ) { my $part_pkg = $cust_pkg->part_pkg;