X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;f=FS%2FFS%2FReport%2FTable.pm;h=e1aec05927022d81da1167b5c36748a8b58360bd;hb=dae45b800de33e67dd8836b4dbb57df7595b03b5;hp=b5805e37f284caf47659e109770e00fbf24f854d;hpb=fb4ab1073f0d15d660c6cdc4e07afebf68ef3924;p=freeside.git diff --git a/FS/FS/Report/Table.pm b/FS/FS/Report/Table.pm index b5805e37f..e1aec0592 100644 --- a/FS/FS/Report/Table.pm +++ b/FS/FS/Report/Table.pm @@ -32,6 +32,35 @@ options in %opt. =over 4 +=item signups: The number of customers signed up. Options are "refnum" +(limit by advertising source) and "indirect" (boolean, tells us to limit +to customers that have a referral_custnum that matches the advertising source). + +=cut + +sub signups { + my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; + my @where = ( $self->in_time_period_and_agent($speriod, $eperiod, $agentnum, + 'cust_main.signupdate') + ); + my $join = ''; + if ( $opt{'indirect'} ) { + $join = " JOIN cust_main AS referring_cust_main". + " ON (cust_main.referral_custnum = referring_cust_main.custnum)"; + + if ( $opt{'refnum'} ) { + push @where, "referring_cust_main.refnum = ".$opt{'refnum'}; + } + } + elsif ( $opt{'refnum'} ) { + push @where, "refnum = ".$opt{'refnum'}; + } + + $self->scalar_sql( + "SELECT COUNT(*) FROM cust_main $join WHERE ".join(' AND ', @where) + ); +} + =item invoiced: The total amount charged on all invoices. =cut @@ -387,6 +416,8 @@ sub cust_bill_pkg_setup { $self->in_time_period_and_agent($speriod, $eperiod, $agentnum), ); + push @where, 'cust_main.refnum = '. $opt{'refnum'} if $opt{'refnum'}; + my $total_sql = "SELECT COALESCE(SUM(cust_bill_pkg.setup),0) FROM cust_bill_pkg $cust_bill_pkg_join @@ -407,6 +438,8 @@ sub cust_bill_pkg_recur { $self->with_classnum($opt{'classnum'}, $opt{'use_override'}), ); + push @where, 'cust_main.refnum = '. $opt{'refnum'} if $opt{'refnum'}; + # subtract all usage from the line item regardless of date my $item_usage; if ( $opt{'project'} ) { @@ -422,8 +455,8 @@ sub cust_bill_pkg_recur { if ( $opt{'distribute'} ) { push @where, "cust_main.agentnum = $agentnum" if $agentnum; push @where, - "$cust_bill_pkg.sdate < $eperiod", - "$cust_bill_pkg.edate > $speriod", + "$cust_bill_pkg.sdate < $eperiod", + "$cust_bill_pkg.edate >= $speriod", ; # the fraction of edate - sdate that's within [speriod, eperiod] $recur_fraction = " * @@ -460,6 +493,8 @@ sub cust_bill_pkg_detail { my @where = ( "cust_bill_pkg.pkgnum != 0" ); + push @where, 'cust_main.refnum = '. $opt{'refnum'} if $opt{'refnum'}; + $agentnum ||= $opt{'agentnum'}; push @where, @@ -546,12 +581,12 @@ sub cust_bill_pkg_discount { } -sub setup_pkg { shift->pkg_field( @_, 'setup' ); } -sub susp_pkg { shift->pkg_field( @_, 'susp' ); } -sub cancel_pkg { shift->pkg_field( @_, 'cancel'); } +sub setup_pkg { shift->pkg_field( 'setup', @_ ); } +sub susp_pkg { shift->pkg_field( 'susp', @_ ); } +sub cancel_pkg { shift->pkg_field( 'cancel', @_ ); } sub pkg_field { - my( $self, $speriod, $eperiod, $agentnum, $field ) = @_; + my( $self, $field, $speriod, $eperiod, $agentnum ) = @_; $self->scalar_sql(" SELECT COUNT(*) FROM cust_pkg LEFT JOIN cust_main USING ( custnum ) @@ -705,14 +740,26 @@ sub extend_projection { my ($speriod, $eperiod) = @_; my %items = map {$_ => 1} @{ $self->{items} }; if ($items{'cust_bill_pkg'}) { - # append, head-to-tail, new line items identical to any that end within the - # period (and aren't expiring) + # What we do here: + # Find all line items that end after the start of the period (and have + # recurring fees, and don't expire before they end). Choose the latest + # one for each package. If it ends before the end of the period, copy + # it forward by one billing period. + # Repeat this until the latest line item for each package no longer ends + # within the period. This is certain to happen in finitely many + # iterations as long as freq > 0. + # - Pg only, obviously. + # - Gives bad results if freq_override is used. my @fields = ( FS::cust_bill_pkg->fields, qw( usage _date expire ) ); my $insert_fields = join(',', @fields); - #advance (sdate, edate) by one billing period + my $add_freq = sub { # emulate FS::part_pkg::add_freq + my $field = shift; + "EXTRACT( EPOCH FROM TO_TIMESTAMP($field) + (CASE WHEN freq ~ E'\\\\D' ". + "THEN freq ELSE freq || 'mon' END)::INTERVAL) AS $field"; + }; foreach (@fields) { if ($_ eq 'edate') { - $_ = '(edate + (edate - sdate)) AS edate' #careful of integer overflow + $_ = $add_freq->('edate'); } elsif ($_ eq 'sdate') { $_ = 'edate AS sdate' @@ -721,20 +768,31 @@ sub extend_projection { $_ = '0 AS setup' #because recurring only } elsif ($_ eq '_date') { - $_ = '(_date + (edate - sdate)) AS _date' + $_ = $add_freq->('_date'); } } my $select_fields = join(',', @fields); my $dbh = dbh; my $sql = + # Subquery here because we need to DISTINCT the whole set, select the + # latest charge per pkgnum, and _then_ check edate < $eperiod + # and edate < expire. "INSERT INTO v_cust_bill_pkg ($insert_fields) - SELECT $select_fields FROM v_cust_bill_pkg - WHERE edate >= $speriod AND edate < $eperiod + SELECT $select_fields FROM ( + SELECT DISTINCT ON (pkgnum) * FROM v_cust_bill_pkg + WHERE edate >= $speriod AND recur > 0 - AND (expire IS NULL OR expire > edate)"; - warn "[extend_projection] $sql\n" if $DEBUG; - my $rows = $dbh->do($sql) or die $dbh->errstr; - warn "[extend_projection] $rows rows\n" if $DEBUG; + AND freq IS NOT NULL + AND freq != '0' + ORDER BY pkgnum, edate DESC + ) AS v1 + WHERE edate < $eperiod AND (edate < expire OR expire IS NULL)"; + my $rows; + do { + warn "[extend_projection] $sql\n" if $DEBUG; + $rows = $dbh->do($sql) or die $dbh->errstr; + warn "[extend_projection] $rows rows\n" if $DEBUG; + } until $rows == 0; } }