X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2FReport%2FTable%2FMonthly.pm;h=f4ba02008f3e83a10d21d1371c05056d65be054d;hp=2d8dd7ee92dac0ffe8e0781a1f46281b43ea8668;hb=32365ef65ca6a40b5262cf166543b1d84c6aa57d;hpb=807f0570749e9093cbc82f6b11b3fa743b86b1db diff --git a/FS/FS/Report/Table/Monthly.pm b/FS/FS/Report/Table/Monthly.pm index 2d8dd7ee9..f4ba02008 100644 --- a/FS/FS/Report/Table/Monthly.pm +++ b/FS/FS/Report/Table/Monthly.pm @@ -1,14 +1,12 @@ package FS::Report::Table::Monthly; use strict; -use vars qw( @ISA $DEBUG ); -use Time::Local; -use FS::UID qw( dbh ); +use vars qw( @ISA ); +use FS::UID qw(dbh); use FS::Report::Table; -use FS::CurrentUser; +use Time::Local qw( timelocal ); @ISA = qw( FS::Report::Table ); -$DEBUG = 0; # turning this on will trace all SQL statements, VERY noisy =head1 NAME @@ -26,6 +24,8 @@ FS::Report::Table::Monthly - Tables of report data, indexed monthly 'end_year' => 2020, #opt 'agentnum' => 54 + 'refnum' => 54 + 'cust_classnum' => [ 1,2,4 ], 'params' => [ [ 'paramsfor', 'item_one' ], [ 'item', 'two' ] ], # ... 'remove_empty' => 1, #collapse empty rows, default 0 'item_labels' => [ ], #useful with remove_empty @@ -33,585 +33,276 @@ FS::Report::Table::Monthly - Tables of report data, indexed monthly my $data = $report->data; -=head1 METHODS +=head1 PARAMETERS -=over 4 +=head2 TIME PERIOD -=item data +C, C, C, and C specify the date +range to be included in the report. The start and end months are included. +Each month's values are summed from midnight on the first of the month to +23:59:59 on the last day of the month. -Returns a hashref of data (!! describe) +=head2 REPORT ITEMS -=cut +=over 4 -sub data { - my $self = shift; +=item items: An arrayref of observables to calculate for each month. See +L for a list of observables and their parameters. - #use Data::Dumper; - #warn Dumper($self); +=item params: An arrayref, parallel to C, of arrayrefs of parameters +(in paired name/value form) to be passed to the observables. - my $smonth = $self->{'start_month'}; - my $syear = $self->{'start_year'}; - my $emonth = $self->{'end_month'}; - my $eyear = $self->{'end_year'}; - my $agentnum = $self->{'agentnum'}; +=item cross_params: Cross-product parameters. This must be an arrayref of +arrayrefs of parameters (paired name/value form). This creates an additional +"axis" (orthogonal to the time and C axes) in which the item is +calculated once with each set of parameters in C. These +parameters are merged with those in C. Instead of being nested two +levels, C will be nested three levels, with the third level +corresponding to this arrayref. - my %data; +=back - while ( $syear < $eyear || ( $syear == $eyear && $smonth < $emonth+1 ) ) { +=head2 FILTERING - if ( $self->{'doublemonths'} ) { - my($firstLabel,$secondLabel) = @{$self->{'doublemonths'}}; - push @{$data{label}}, "$smonth/$syear $firstLabel"; - push @{$data{label}}, "$smonth/$syear $secondLabel"; - } - else { - push @{$data{label}}, "$smonth/$syear"; - } +=over 4 - my $speriod = timelocal(0,0,0,1,$smonth-1,$syear); - push @{$data{speriod}}, $speriod; - if ( ++$smonth == 13 ) { $syear++; $smonth=1; } - my $eperiod = timelocal(0,0,0,1,$smonth-1,$syear); - push @{$data{eperiod}}, $eperiod; - - my $col = 0; - my @items = @{$self->{'items'}}; - my $i; - for ( $i = 0; $i < scalar(@items); $i++ ) { - if ( $self->{'doublemonths'} ) { - my $item = $items[$i]; - my @param = $self->{'params'} ? @{ $self->{'params'}[$i] }: (); - my $value = $self->$item($speriod, $eperiod, $agentnum, @param); - push @{$data{data}->[$col]}, $value; - $item = $items[$i+1]; - @param = $self->{'params'} ? @{ $self->{'params'}[++$i] }: (); - $value = $self->$item($speriod, $eperiod, $agentnum, @param); - push @{$data{data}->[$col++]}, $value; - } - else { - my $item = $items[$i]; - my @param = $self->{'params'} ? @{ $self->{'params'}[$col] }: (); - my $value = $self->$item($speriod, $eperiod, $agentnum, @param); - push @{$data{data}->[$col++]}, $value; - } - } +=item agentnum: Limit to customers with this agent. - } +=item refnum: Limit to customers with this advertising source. - #these need to get generalized, sheesh - $data{'items'} = $self->{'items'}; - $data{'item_labels'} = $self->{'item_labels'} || $self->{'items'}; - $data{'colors'} = $self->{'colors'}; - $data{'links'} = $self->{'links'} || []; +=item cust_classnum: Limit to customers with this classnum; can be an +arrayref. - #use Data::Dumper; - #warn Dumper(\%data); +=item remove_empty: Set this to a true value to hide rows that contain +only zeroes. The C array in the returned data will list the item +indices that are actually present in the output so that you know what they +are. Ignored if C is in effect. - if ( $self->{'remove_empty'} ) { +=back - #warn "removing empty rows\n"; +=head2 PASS-THROUGH - my $col = 0; - #these need to get generalized, sheesh - my @newitems = (); - my @newlabels = (); - my @newdata = (); - my @newcolors = (); - my @newlinks = (); - foreach my $item ( @{$self->{'items'}} ) { +C, C, and C may be specified as arrayrefs +parallel to C. Those values will be returned in C, with any +hidden rows (due to C) filtered out, which is the only +reason to do this. Now that we have C it's probably better to +use that. - if ( grep { $_ != 0 } @{$data{'data'}->[$col]} ) { - push @newitems, $data{'items'}->[$col]; - push @newlabels, $data{'item_labels'}->[$col]; - push @newdata, $data{'data'}->[$col]; - push @newcolors, $data{'colors'}->[$col]; - push @newlinks, $data{'links'}->[$col]; - } +=item PROCESSING - $col++; - } +=item normalize: Set this to an item index to have all other items expressed +as a percentage of that one. That item will then be omitted from the output. +If the normalization item is zero in some period, all the values in that +period will be undef. - $data{'items'} = \@newitems; - $data{'item_labels'} = \@newlabels; - $data{'data'} = \@newdata; - $data{'colors'} = \@newcolors; - $data{'links'} = \@newlinks; +=head1 RETURNED DATA - } +The C method runs the report and returns a hashref of the following: - #use Data::Dumper; - #warn Dumper(\%data); - - \%data; - -} +=over 4 -sub invoiced { #invoiced - my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; +=item label - $self->scalar_sql(" - SELECT SUM(charged) - FROM cust_bill - LEFT JOIN cust_main USING ( custnum ) - WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum) - . (%opt ? $self->for_custnum(%opt) : '') - ); - -} +Month labels, in MM/YYYY format. -sub netsales { #net sales - my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; +=item speriod, eperiod - $self->invoiced($speriod,$eperiod,$agentnum,%opt) - - $self->netcredits($speriod,$eperiod,$agentnum,%opt); -} +Absolute start and end times of each month, in unix time format. -#deferred revenue +=item items -sub cashflow { - my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; +The values passed in as C, with any suppressed rows deleted. - $self->payments($speriod, $eperiod, $agentnum, %opt) - - $self->refunds( $speriod, $eperiod, $agentnum, %opt); -} +=item indices -sub netcashflow { - my( $self, $speriod, $eperiod, $agentnum ) = @_; +The indices of items in the input C list that appear in the result +set. Useful for figuring out what they are when C has deleted +some items. - $self->receipts($speriod, $eperiod, $agentnum) - - $self->netrefunds( $speriod, $eperiod, $agentnum); -} +=item item_labels, colors, links - see PASS-THROUGH above -sub payments { - my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; - $self->scalar_sql(" - SELECT SUM(paid) - FROM cust_pay - LEFT JOIN cust_main USING ( custnum ) - WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum) - . (%opt ? $self->for_custnum(%opt) : '') - ); -} +=item data -sub credits { - my( $self, $speriod, $eperiod, $agentnum ) = @_; - $self->scalar_sql(" - SELECT SUM(amount) - FROM cust_credit - LEFT JOIN cust_main USING ( custnum ) - WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum) - ); -} +The actual results. An arrayref corresponding to C