diff options
| author | levinse <levinse> | 2011-05-13 20:03:20 +0000 | 
|---|---|---|
| committer | levinse <levinse> | 2011-05-13 20:03:20 +0000 | 
| commit | 20950bb21ee5dd8839a05dfcd58efa0a98e48e5a (patch) | |
| tree | f7bd53f02fc3ca3aeb0414f523ce9809cc873fc2 | |
| parent | 22a59960edd8f446d82c60f9ca5ac9b957ef2eac (diff) | |
create a Sales, Credits, and Receipts report broken down by day, RT11470
| -rw-r--r-- | FS/FS/Mason.pm | 2 | ||||
| -rw-r--r-- | FS/FS/Report/Table.pm | 470 | ||||
| -rw-r--r-- | FS/FS/Report/Table/Daily.pm | 140 | ||||
| -rw-r--r-- | FS/FS/Report/Table/Monthly.pm | 479 | ||||
| -rw-r--r-- | httemplate/elements/menu.html | 1 | ||||
| -rw-r--r-- | httemplate/graph/elements/monthly.html | 61 | ||||
| -rw-r--r-- | httemplate/graph/money_time_daily.cgi | 111 | ||||
| -rw-r--r-- | httemplate/graph/report_money_time_daily.html | 26 | 
8 files changed, 791 insertions, 499 deletions
| diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm index e74f44eb1..07e650a41 100644 --- a/FS/FS/Mason.pm +++ b/FS/FS/Mason.pm @@ -133,7 +133,9 @@ if ( -e $addl_handler_use_file ) {                   );    use FS::Misc::eps2png qw( eps2png );    use FS::Report::FCC_477; +  use FS::Report::Table;    use FS::Report::Table::Monthly; +  use FS::Report::Table::Daily;    use FS::TicketSystem;    use FS::NetworkMonitoringSystem;    use FS::Tron qw( tron_lint ); diff --git a/FS/FS/Report/Table.pm b/FS/FS/Report/Table.pm index 9f636fa43..113bf8d9e 100644 --- a/FS/FS/Report/Table.pm +++ b/FS/FS/Report/Table.pm @@ -1,9 +1,14 @@  package FS::Report::Table;  use strict; -use vars qw( @ISA ); +use vars qw( @ISA $DEBUG );  use FS::Report; +use Time::Local qw( timelocal ); +use FS::UID qw( dbh ); +use FS::Report::Table; +use FS::CurrentUser; +$DEBUG = 0; # turning this on will trace all SQL statements, VERY noisy  @ISA = qw( FS::Report );  =head1 NAME @@ -14,6 +19,469 @@ FS::Report::Table - Tables of report data  See the more specific report objects, currently only FS::Report::Table::Monthly +=cut + +sub invoiced { #invoiced +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; + +  $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) : '') +  ); +   +} + +sub netsales { #net sales +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; + +    $self->invoiced($speriod,$eperiod,$agentnum,%opt) +  - $self->netcredits($speriod,$eperiod,$agentnum,%opt); +} + +#deferred revenue + +sub cashflow { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; + +    $self->payments($speriod, $eperiod, $agentnum, %opt) +  - $self->refunds( $speriod, $eperiod, $agentnum, %opt); +} + +sub netcashflow { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; + +    $self->receipts($speriod, $eperiod, $agentnum) +  - $self->netrefunds( $speriod, $eperiod, $agentnum); +} + +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) : '') +  ); +} + +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) +  ); +} + +sub refunds { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; +  $self->scalar_sql(" +    SELECT SUM(refund) +      FROM cust_refund +        LEFT JOIN cust_main USING ( custnum ) +      WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum) +      . (%opt ? $self->for_custnum(%opt) : '') +  ); +} + +sub netcredits { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; +  $self->scalar_sql(" +    SELECT SUM(cust_credit_bill.amount) +      FROM cust_credit_bill +        LEFT JOIN cust_bill USING ( invnum  ) +        LEFT JOIN cust_main USING ( custnum ) +      WHERE ". $self->in_time_period_and_agent( $speriod, +                                                $eperiod, +                                                $agentnum, +                                                'cust_bill._date' +                                              ) +      . (%opt ? $self->for_custnum(%opt) : '') +  ); +} + +sub receipts { #net payments +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $self->scalar_sql(" +    SELECT SUM(cust_bill_pay.amount) +      FROM cust_bill_pay +        LEFT JOIN cust_bill USING ( invnum  ) +        LEFT JOIN cust_main USING ( custnum ) +      WHERE ". $self->in_time_period_and_agent( $speriod, +                                                $eperiod, +                                                $agentnum, +                                                'cust_bill._date' +                                              ) +  ); +} + +sub netrefunds { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $self->scalar_sql(" +    SELECT SUM(cust_credit_refund.amount) +      FROM cust_credit_refund +        LEFT JOIN cust_credit USING ( crednum  ) +        LEFT JOIN cust_main   USING ( custnum ) +      WHERE ". $self->in_time_period_and_agent( $speriod, +                                                $eperiod, +                                                $agentnum, +                                                'cust_credit._date' +                                              ) +  ); +} + +#these should be auto-generated or $AUTOLOADed or something +sub invoiced_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->invoiced($speriod, $eperiod, $agentnum); +} + +sub netsales_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->netsales($speriod, $eperiod, $agentnum); +} + +sub receipts_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->receipts($speriod, $eperiod, $agentnum); +} + +sub payments_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->payments($speriod, $eperiod, $agentnum); +} + +sub credits_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->credits($speriod, $eperiod, $agentnum); +} + +sub netcredits_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->netcredits($speriod, $eperiod, $agentnum); +} + +sub cashflow_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->cashflow($speriod, $eperiod, $agentnum); +} + +sub netcashflow_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->cashflow($speriod, $eperiod, $agentnum); +} + +sub refunds_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->refunds($speriod, $eperiod, $agentnum); +} + +sub netrefunds_12mo { +  my( $self, $speriod, $eperiod, $agentnum ) = @_; +  $speriod = $self->_subtract_11mo($speriod); +  $self->netrefunds($speriod, $eperiod, $agentnum); +} + + +#not being too bad with the false laziness +sub _subtract_11mo { +  my($self, $time) = @_; +  my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($time) )[0,1,2,3,4,5]; +  $mon -= 11; +  if ( $mon < 0 ) { $mon+=12; $year--; } +  timelocal($sec,$min,$hour,$mday,$mon,$year); +} + +sub cust_pkg_setup_cost { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; +  my $where = ''; +  my $comparison = ''; +  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { +    if ( $1 == 0 ) { +      $comparison = 'IS NULL'; +    } +    else { +      $comparison = "= $1"; +    } +    $where = "AND part_pkg.classnum $comparison"; +  } +  $agentnum ||= $opt{'agentnum'}; + +  my $total_sql = " SELECT SUM(part_pkg.setup_cost) "; +  $total_sql .= " FROM cust_pkg  +             LEFT JOIN cust_main USING ( custnum ) +             LEFT JOIN part_pkg  USING ( pkgpart ) +                  WHERE pkgnum != 0 +                  $where +                  AND ".$self->in_time_period_and_agent( +                    $speriod, $eperiod, $agentnum, 'cust_pkg.setup'); +  return $self->scalar_sql($total_sql); +} + +sub cust_pkg_recur_cost { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; +  my $where = ''; +  my $comparison = ''; +  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { +    if ( $1 == 0 ) { +      $comparison = 'IS NULL'; +    } +    else { +      $comparison = "= $1"; +    } +    $where = " AND part_pkg.classnum $comparison"; +  } +  $agentnum ||= $opt{'agentnum'}; +  # duplication of in_time_period_and_agent +  # because we do it a little differently here +  $where .= " AND cust_main.agentnum = $agentnum" if $agentnum; +  $where .= " AND ". +          $FS::CurrentUser::CurrentUser->agentnums_sql('table' => 'cust_main'); + +  my $total_sql = " SELECT SUM(part_pkg.recur_cost) "; +  $total_sql .= " FROM cust_pkg +             LEFT JOIN cust_main USING ( custnum ) +             LEFT JOIN part_pkg  USING ( pkgpart ) +                  WHERE pkgnum != 0 +                  $where +                  AND cust_pkg.setup < $eperiod +                  AND (cust_pkg.cancel > $speriod OR cust_pkg.cancel IS NULL) +                  "; +  return $self->scalar_sql($total_sql); +} +  +sub cust_bill_pkg { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; + +  my $where = ''; +  my $comparison = ''; +  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { +    if ( $1 == 0 ) { +      $comparison = "IS NULL"; +    } else { +      $comparison = "= $1"; +    } + +    if ( $opt{'use_override'} ) { +      $where = "AND ( +        part_pkg.classnum $comparison AND pkgpart_override IS NULL OR +        override.classnum $comparison AND pkgpart_override IS NOT NULL +      )"; +    } else { +      $where = "AND part_pkg.classnum $comparison"; +    } +  } + +  $agentnum ||= $opt{'agentnum'}; + +  my $total_sql = +    " SELECT COALESCE( SUM(cust_bill_pkg.setup + cust_bill_pkg.recur), 0 ) "; + +  $total_sql .= +    " / CASE COUNT(cust_pkg.*) WHEN 0 THEN 1 ELSE COUNT(cust_pkg.*) END " +      if $opt{average_per_cust_pkg}; + +  $total_sql .= +    " FROM cust_bill_pkg +        LEFT JOIN cust_bill USING ( invnum ) +        LEFT JOIN cust_main USING ( custnum ) +        LEFT JOIN cust_pkg USING ( pkgnum ) +        LEFT JOIN part_pkg USING ( pkgpart ) +        LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart +      WHERE pkgnum != 0 +        $where +        AND ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum); +   +  if ($opt{use_usage} && $opt{use_usage} eq 'recurring') { +    my $total = $self->scalar_sql($total_sql); +    my $usage = cust_bill_pkg_detail(@_); #$speriod, $eperiod, $agentnum, %opt  +    return $total-$usage; +  } elsif ($opt{use_usage} && $opt{use_usage} eq 'usage') { +    return cust_bill_pkg_detail(@_); #$speriod, $eperiod, $agentnum, %opt  +  } else { +    return $self->scalar_sql($total_sql); +  } +} + +sub cust_bill_pkg_detail { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; + +  my @where = ( "cust_bill_pkg.pkgnum != 0" ); +  my $comparison = ''; +  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { +    if ( $1 == 0 ) { +      $comparison = "IS NULL"; +    } else { +      $comparison = "= $1"; +    } + +    if ( $opt{'use_override'} ) { +      push @where, "( +        part_pkg.classnum $comparison AND pkgpart_override IS NULL OR +        override.classnum $comparison AND pkgpart_override IS NOT NULL +      )"; +    } else { +      push @where, "part_pkg.classnum $comparison"; +    } +  } + +  if ( $opt{'usageclass'} =~ /^(\d+)$/ ) { +    if ( $1 == 0 ) { +      $comparison = "IS NULL"; +    } else { +      $comparison = "= $1"; +    } + +    push @where, "cust_bill_pkg_detail.classnum $comparison"; +  } + +  $agentnum ||= $opt{'agentnum'}; + +  my $where = join( ' AND ', @where ); + +  my $total_sql = " SELECT SUM(amount) "; + +  $total_sql .= +    " / CASE COUNT(cust_pkg.*) WHEN 0 THEN 1 ELSE COUNT(cust_pkg.*) END " +      if $opt{average_per_cust_pkg}; + +  $total_sql .= +    " FROM cust_bill_pkg_detail +        LEFT JOIN cust_bill_pkg USING ( billpkgnum ) +        LEFT JOIN cust_bill ON cust_bill_pkg.invnum = cust_bill.invnum +        LEFT JOIN cust_main USING ( custnum ) +        LEFT JOIN cust_pkg ON cust_bill_pkg.pkgnum = cust_pkg.pkgnum +        LEFT JOIN part_pkg USING ( pkgpart ) +        LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart +      WHERE $where +        AND ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum); + +  $self->scalar_sql($total_sql); +   +} + +sub cust_bill_pkg_discount { +  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; + +  #my $where = ''; +  #my $comparison = ''; +  #if ( $opt{'classnum'} =~ /^(\d+)$/ ) { +  #  if ( $1 == 0 ) { +  #    $comparison = "IS NULL"; +  #  } else { +  #    $comparison = "= $1"; +  #  } +  # +  #  if ( $opt{'use_override'} ) { +  #    $where = "( +  #      part_pkg.classnum $comparison AND pkgpart_override IS NULL OR +  #      override.classnum $comparison AND pkgpart_override IS NOT NULL +  #    )"; +  #  } else { +  #    $where = "part_pkg.classnum $comparison"; +  #  } +  #} + +  $agentnum ||= $opt{'agentnum'}; + +  my $total_sql = +    " SELECT COALESCE( SUM( cust_bill_pkg_discount.amount ), 0 ) "; + +  #$total_sql .= +  #  " / CASE COUNT(cust_pkg.*) WHEN 0 THEN 1 ELSE COUNT(cust_pkg.*) END " +  #    if $opt{average_per_cust_pkg}; + +  $total_sql .= +    " FROM cust_bill_pkg_discount +        LEFT JOIN cust_bill_pkg USING ( billpkgnum ) +        LEFT JOIN cust_bill USING ( invnum ) +        LEFT JOIN cust_main USING ( custnum ) +      WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum); +  #      LEFT JOIN cust_pkg_discount USING ( pkgdiscountnum ) +  #      LEFT JOIN discount USING ( discountnum ) +  #      LEFT JOIN cust_pkg USING ( pkgnum ) +  #      LEFT JOIN part_pkg USING ( pkgpart ) +  #      LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart +   +  return $self->scalar_sql($total_sql); + +} + +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 ) = @_; +  $self->scalar_sql(" +    SELECT COUNT(*) FROM cust_pkg +        LEFT JOIN cust_main USING ( custnum ) +      WHERE ". $self->in_time_period_and_agent( $speriod, +                                                $eperiod, +                                                $agentnum, +                                                "cust_pkg.$field", +                                              ) +  ); + +} + +#this is going to be harder.. +#sub unsusp_pkg { +#  my( $self, $speriod, $eperiod, $agentnum ) = @_; +#  $self->scalar_sql(" +#    SELECT COUNT(*) FROM h_cust_pkg +#      WHERE  +# +#} + +sub in_time_period_and_agent { +  my( $self, $speriod, $eperiod, $agentnum ) = splice(@_, 0, 4); +  my $col = @_ ? shift() : '_date'; + +  my $sql = "$col >= $speriod AND $col < $eperiod"; + +  #agent selection +  $sql .= " AND cust_main.agentnum = $agentnum" +    if $agentnum; + +  #agent virtualization +  $sql .= ' AND '. +          $FS::CurrentUser::CurrentUser->agentnums_sql( 'table'=>'cust_main' ); + +  $sql; +} + +sub for_custnum { +    my ( $self, %opt ) = @_; +    return '' unless $opt{'custnum'}; +    $opt{'custnum'} =~ /^\d+$/ ? " and custnum = $opt{custnum} " : ''; +} + +sub scalar_sql { +  my( $self, $sql ) = ( shift, shift ); +  my $sth = dbh->prepare($sql) or die dbh->errstr; +  warn "FS::Report::Table::Monthly\n$sql\n" if $DEBUG; +  $sth->execute +    or die "Unexpected error executing statement $sql: ". $sth->errstr; +  $sth->fetchrow_arrayref->[0] || 0; +} + +=back +  =head1 BUGS  Documentation. diff --git a/FS/FS/Report/Table/Daily.pm b/FS/FS/Report/Table/Daily.pm new file mode 100644 index 000000000..5bbd6e4e7 --- /dev/null +++ b/FS/FS/Report/Table/Daily.pm @@ -0,0 +1,140 @@ +package FS::Report::Table::Daily; + +use strict; +use vars qw( @ISA ); +use FS::Report::Table; +use Time::Local qw( timelocal ); + +@ISA = qw( FS::Report::Table ); + +=head1 NAME + +FS::Report::Table::Daily - Tables of report data, indexed daily + +=head1 SYNOPSIS + +  use FS::Report::Table::Daily; + +  my $report = new FS::Report::Table::Daily ( +    'items' => [ 'invoiced', 'netsales', 'credits', 'receipts', ], +    'start_month' => 4, +    'start_year'  => 2000, +    'end_month'   => 4, +    'end_year'    => 2020, +    'start_day'   => 2, +    'end_day'     => 27, +    #opt +    'agentnum'    => 54 +    'params'      => [ [ 'paramsfor', 'item_one' ], [ 'item', 'two' ] ], # ... +    'remove_empty' => 1, #collapse empty rows, default 0 +    'item_labels' => [ ], #useful with remove_empty +  ); + +  my $data = $report->data; + +=head1 METHODS + +=over 4 + +=item data + +Returns a hashref of data (!! describe) + +=cut + +sub data { +  my $self = shift; + +  my $sday = $self->{'start_day'}; +  my $smonth = $self->{'start_month'}; +  my $syear = $self->{'start_year'}; +  my $eday = $self->{'end_day'}; +  my $emonth = $self->{'end_month'}; +  my $eyear = $self->{'end_year'}; +  my $agentnum = $self->{'agentnum'}; + +  my %data; + +  my $sdate = timelocal(0,0,0,$sday,$smonth-1,$syear); +  my $edate = timelocal(0,0,0,$eday,$emonth-1,$eyear); + +  warn "daily range $sdate $edate\n"; + +  # XXX: use date_format config for the labels since we have day in the labels now? +  # XXX: leap seconds / DST  +  while ( $sdate < $edate ) { +    my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($sdate); +    $mon++; +    $year += 1900; +    warn "label=$mday/$mon/$year\n"; +    push @{$data{label}}, "$mday/$mon/$year"; + +    my $speriod = $sdate; +    $sdate += 86400; +    my $eperiod = $sdate; + +    push @{$data{speriod}}, $speriod; +    push @{$data{eperiod}}, $eperiod; + +    my $col = 0; +    my @items = @{$self->{'items'}}; +    my $i; +    for ( $i = 0; $i < scalar(@items); $i++ ) { +	  my $item = $items[$i]; +	  my @param = $self->{'params'} ? @{ $self->{'params'}[$col] }: (); +	  my $value = $self->$item($speriod, $eperiod, $agentnum, @param); +	  push @{$data{data}->[$col++]}, $value; +    } +  } + +  #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'} || []; + +  if ( $self->{'remove_empty'} ) { + +    my $col = 0; +    #these need to get generalized, sheesh +    my @newitems = (); +    my @newlabels = (); +    my @newdata = (); +    my @newcolors = (); +    my @newlinks = (); +    foreach my $item ( @{$self->{'items'}} ) { + +      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]; +      } + +      $col++; +    } + +    $data{'items'}       = \@newitems; +    $data{'item_labels'} = \@newlabels; +    $data{'data'}        = \@newdata; +    $data{'colors'}      = \@newcolors; +    $data{'links'}       = \@newlinks; + +  } + +  \%data; + +} + +=back + +=head1 BUGS + +Documentation. + +=head1 SEE ALSO + +=cut + +1; diff --git a/FS/FS/Report/Table/Monthly.pm b/FS/FS/Report/Table/Monthly.pm index 2d8dd7ee9..f57fb064b 100644 --- a/FS/FS/Report/Table/Monthly.pm +++ b/FS/FS/Report/Table/Monthly.pm @@ -1,14 +1,11 @@  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::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 @@ -46,9 +43,6 @@ Returns a hashref of data (!! describe)  sub data {    my $self = shift; -  #use Data::Dumper; -  #warn Dumper($self); -    my $smonth = $self->{'start_month'};    my $syear = $self->{'start_year'};    my $emonth = $self->{'end_month'}; @@ -104,13 +98,8 @@ sub data {    $data{'colors'}      = $self->{'colors'};    $data{'links'}       = $self->{'links'} || []; -  #use Data::Dumper; -  #warn Dumper(\%data); -    if ( $self->{'remove_empty'} ) { -    #warn "removing empty rows\n"; -      my $col = 0;      #these need to get generalized, sheesh      my @newitems = (); @@ -139,471 +128,7 @@ sub data {    } -  #use Data::Dumper; -  #warn Dumper(\%data); -    \%data; - -} - -sub invoiced { #invoiced -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; - -  $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) : '') -  ); -   -} - -sub netsales { #net sales -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; - -    $self->invoiced($speriod,$eperiod,$agentnum,%opt) -  - $self->netcredits($speriod,$eperiod,$agentnum,%opt); -} - -#deferred revenue - -sub cashflow { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; - -    $self->payments($speriod, $eperiod, $agentnum, %opt) -  - $self->refunds( $speriod, $eperiod, $agentnum, %opt); -} - -sub netcashflow { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; - -    $self->receipts($speriod, $eperiod, $agentnum) -  - $self->netrefunds( $speriod, $eperiod, $agentnum); -} - -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) : '') -  ); -} - -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) -  ); -} - -sub refunds { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; -  $self->scalar_sql(" -    SELECT SUM(refund) -      FROM cust_refund -        LEFT JOIN cust_main USING ( custnum ) -      WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum) -      . (%opt ? $self->for_custnum(%opt) : '') -  ); -} - -sub netcredits { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; -  $self->scalar_sql(" -    SELECT SUM(cust_credit_bill.amount) -      FROM cust_credit_bill -        LEFT JOIN cust_bill USING ( invnum  ) -        LEFT JOIN cust_main USING ( custnum ) -      WHERE ". $self->in_time_period_and_agent( $speriod, -                                                $eperiod, -                                                $agentnum, -                                                'cust_bill._date' -                                              ) -      . (%opt ? $self->for_custnum(%opt) : '') -  ); -} - -sub receipts { #net payments -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $self->scalar_sql(" -    SELECT SUM(cust_bill_pay.amount) -      FROM cust_bill_pay -        LEFT JOIN cust_bill USING ( invnum  ) -        LEFT JOIN cust_main USING ( custnum ) -      WHERE ". $self->in_time_period_and_agent( $speriod, -                                                $eperiod, -                                                $agentnum, -                                                'cust_bill._date' -                                              ) -  ); -} - -sub netrefunds { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $self->scalar_sql(" -    SELECT SUM(cust_credit_refund.amount) -      FROM cust_credit_refund -        LEFT JOIN cust_credit USING ( crednum  ) -        LEFT JOIN cust_main   USING ( custnum ) -      WHERE ". $self->in_time_period_and_agent( $speriod, -                                                $eperiod, -                                                $agentnum, -                                                'cust_credit._date' -                                              ) -  ); -} - -#these should be auto-generated or $AUTOLOADed or something -sub invoiced_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->invoiced($speriod, $eperiod, $agentnum); -} - -sub netsales_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->netsales($speriod, $eperiod, $agentnum); -} - -sub receipts_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->receipts($speriod, $eperiod, $agentnum); -} - -sub payments_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->payments($speriod, $eperiod, $agentnum); -} - -sub credits_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->credits($speriod, $eperiod, $agentnum); -} - -sub netcredits_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->netcredits($speriod, $eperiod, $agentnum); -} - -sub cashflow_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->cashflow($speriod, $eperiod, $agentnum); -} - -sub netcashflow_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->cashflow($speriod, $eperiod, $agentnum); -} - -sub refunds_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->refunds($speriod, $eperiod, $agentnum); -} - -sub netrefunds_12mo { -  my( $self, $speriod, $eperiod, $agentnum ) = @_; -  $speriod = $self->_subtract_11mo($speriod); -  $self->netrefunds($speriod, $eperiod, $agentnum); -} - - -#not being too bad with the false laziness -use Time::Local qw(timelocal); -sub _subtract_11mo { -  my($self, $time) = @_; -  my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($time) )[0,1,2,3,4,5]; -  $mon -= 11; -  if ( $mon < 0 ) { $mon+=12; $year--; } -  timelocal($sec,$min,$hour,$mday,$mon,$year); -} - -sub cust_pkg_setup_cost { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; -  my $where = ''; -  my $comparison = ''; -  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { -    if ( $1 == 0 ) { -      $comparison = 'IS NULL'; -    } -    else { -      $comparison = "= $1"; -    } -    $where = "AND part_pkg.classnum $comparison"; -  } -  $agentnum ||= $opt{'agentnum'}; - -  my $total_sql = " SELECT SUM(part_pkg.setup_cost) "; -  $total_sql .= " FROM cust_pkg  -             LEFT JOIN cust_main USING ( custnum ) -             LEFT JOIN part_pkg  USING ( pkgpart ) -                  WHERE pkgnum != 0 -                  $where -                  AND ".$self->in_time_period_and_agent( -                    $speriod, $eperiod, $agentnum, 'cust_pkg.setup'); -  return $self->scalar_sql($total_sql); -} - -sub cust_pkg_recur_cost { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; -  my $where = ''; -  my $comparison = ''; -  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { -    if ( $1 == 0 ) { -      $comparison = 'IS NULL'; -    } -    else { -      $comparison = "= $1"; -    } -    $where = " AND part_pkg.classnum $comparison"; -  } -  $agentnum ||= $opt{'agentnum'}; -  # duplication of in_time_period_and_agent -  # because we do it a little differently here -  $where .= " AND cust_main.agentnum = $agentnum" if $agentnum; -  $where .= " AND ". -          $FS::CurrentUser::CurrentUser->agentnums_sql('table' => 'cust_main'); - -  my $total_sql = " SELECT SUM(part_pkg.recur_cost) "; -  $total_sql .= " FROM cust_pkg -             LEFT JOIN cust_main USING ( custnum ) -             LEFT JOIN part_pkg  USING ( pkgpart ) -                  WHERE pkgnum != 0 -                  $where -                  AND cust_pkg.setup < $eperiod -                  AND (cust_pkg.cancel > $speriod OR cust_pkg.cancel IS NULL) -                  "; -  return $self->scalar_sql($total_sql); -} -  -sub cust_bill_pkg { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; - -  my $where = ''; -  my $comparison = ''; -  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { -    if ( $1 == 0 ) { -      $comparison = "IS NULL"; -    } else { -      $comparison = "= $1"; -    } - -    if ( $opt{'use_override'} ) { -      $where = "AND ( -        part_pkg.classnum $comparison AND pkgpart_override IS NULL OR -        override.classnum $comparison AND pkgpart_override IS NOT NULL -      )"; -    } else { -      $where = "AND part_pkg.classnum $comparison"; -    } -  } - -  $agentnum ||= $opt{'agentnum'}; - -  my $total_sql = -    " SELECT COALESCE( SUM(cust_bill_pkg.setup + cust_bill_pkg.recur), 0 ) "; - -  $total_sql .= -    " / CASE COUNT(cust_pkg.*) WHEN 0 THEN 1 ELSE COUNT(cust_pkg.*) END " -      if $opt{average_per_cust_pkg}; - -  $total_sql .= -    " FROM cust_bill_pkg -        LEFT JOIN cust_bill USING ( invnum ) -        LEFT JOIN cust_main USING ( custnum ) -        LEFT JOIN cust_pkg USING ( pkgnum ) -        LEFT JOIN part_pkg USING ( pkgpart ) -        LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart -      WHERE pkgnum != 0 -        $where -        AND ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum); -   -  if ($opt{use_usage} && $opt{use_usage} eq 'recurring') { -    my $total = $self->scalar_sql($total_sql); -    my $usage = cust_bill_pkg_detail(@_); #$speriod, $eperiod, $agentnum, %opt  -    return $total-$usage; -  } elsif ($opt{use_usage} && $opt{use_usage} eq 'usage') { -    return cust_bill_pkg_detail(@_); #$speriod, $eperiod, $agentnum, %opt  -  } else { -    return $self->scalar_sql($total_sql); -  } -} - -sub cust_bill_pkg_detail { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; - -  my @where = ( "cust_bill_pkg.pkgnum != 0" ); -  my $comparison = ''; -  if ( $opt{'classnum'} =~ /^(\d+)$/ ) { -    if ( $1 == 0 ) { -      $comparison = "IS NULL"; -    } else { -      $comparison = "= $1"; -    } - -    if ( $opt{'use_override'} ) { -      push @where, "( -        part_pkg.classnum $comparison AND pkgpart_override IS NULL OR -        override.classnum $comparison AND pkgpart_override IS NOT NULL -      )"; -    } else { -      push @where, "part_pkg.classnum $comparison"; -    } -  } - -  if ( $opt{'usageclass'} =~ /^(\d+)$/ ) { -    if ( $1 == 0 ) { -      $comparison = "IS NULL"; -    } else { -      $comparison = "= $1"; -    } - -    push @where, "cust_bill_pkg_detail.classnum $comparison"; -  } - -  $agentnum ||= $opt{'agentnum'}; - -  my $where = join( ' AND ', @where ); - -  my $total_sql = " SELECT SUM(amount) "; - -  $total_sql .= -    " / CASE COUNT(cust_pkg.*) WHEN 0 THEN 1 ELSE COUNT(cust_pkg.*) END " -      if $opt{average_per_cust_pkg}; - -  $total_sql .= -    " FROM cust_bill_pkg_detail -        LEFT JOIN cust_bill_pkg USING ( billpkgnum ) -        LEFT JOIN cust_bill ON cust_bill_pkg.invnum = cust_bill.invnum -        LEFT JOIN cust_main USING ( custnum ) -        LEFT JOIN cust_pkg ON cust_bill_pkg.pkgnum = cust_pkg.pkgnum -        LEFT JOIN part_pkg USING ( pkgpart ) -        LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart -      WHERE $where -        AND ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum); - -  $self->scalar_sql($total_sql); -   -} - -sub cust_bill_pkg_discount { -  my( $self, $speriod, $eperiod, $agentnum, %opt ) = @_; - -  #my $where = ''; -  #my $comparison = ''; -  #if ( $opt{'classnum'} =~ /^(\d+)$/ ) { -  #  if ( $1 == 0 ) { -  #    $comparison = "IS NULL"; -  #  } else { -  #    $comparison = "= $1"; -  #  } -  # -  #  if ( $opt{'use_override'} ) { -  #    $where = "( -  #      part_pkg.classnum $comparison AND pkgpart_override IS NULL OR -  #      override.classnum $comparison AND pkgpart_override IS NOT NULL -  #    )"; -  #  } else { -  #    $where = "part_pkg.classnum $comparison"; -  #  } -  #} - -  $agentnum ||= $opt{'agentnum'}; - -  my $total_sql = -    " SELECT COALESCE( SUM( cust_bill_pkg_discount.amount ), 0 ) "; - -  #$total_sql .= -  #  " / CASE COUNT(cust_pkg.*) WHEN 0 THEN 1 ELSE COUNT(cust_pkg.*) END " -  #    if $opt{average_per_cust_pkg}; - -  $total_sql .= -    " FROM cust_bill_pkg_discount -        LEFT JOIN cust_bill_pkg USING ( billpkgnum ) -        LEFT JOIN cust_bill USING ( invnum ) -        LEFT JOIN cust_main USING ( custnum ) -      WHERE ". $self->in_time_period_and_agent($speriod, $eperiod, $agentnum); -  #      LEFT JOIN cust_pkg_discount USING ( pkgdiscountnum ) -  #      LEFT JOIN discount USING ( discountnum ) -  #      LEFT JOIN cust_pkg USING ( pkgnum ) -  #      LEFT JOIN part_pkg USING ( pkgpart ) -  #      LEFT JOIN part_pkg AS override ON pkgpart_override = override.pkgpart -   -  return $self->scalar_sql($total_sql); - -} - -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 ) = @_; -  $self->scalar_sql(" -    SELECT COUNT(*) FROM cust_pkg -        LEFT JOIN cust_main USING ( custnum ) -      WHERE ". $self->in_time_period_and_agent( $speriod, -                                                $eperiod, -                                                $agentnum, -                                                "cust_pkg.$field", -                                              ) -  ); - -} - -#this is going to be harder.. -#sub unsusp_pkg { -#  my( $self, $speriod, $eperiod, $agentnum ) = @_; -#  $self->scalar_sql(" -#    SELECT COUNT(*) FROM h_cust_pkg -#      WHERE  -# -#} - -sub in_time_period_and_agent { -  my( $self, $speriod, $eperiod, $agentnum ) = splice(@_, 0, 4); -  my $col = @_ ? shift() : '_date'; - -  my $sql = "$col >= $speriod AND $col < $eperiod"; - -  #agent selection -  $sql .= " AND cust_main.agentnum = $agentnum" -    if $agentnum; - -  #agent virtualization -  $sql .= ' AND '. -          $FS::CurrentUser::CurrentUser->agentnums_sql( 'table'=>'cust_main' ); - -  $sql; -} - -sub for_custnum { -    my ( $self, %opt ) = @_; -    return '' unless $opt{'custnum'}; -    $opt{'custnum'} =~ /^\d+$/ ? " and custnum = $opt{custnum} " : ''; -} - -sub scalar_sql { -  my( $self, $sql ) = ( shift, shift ); -  my $sth = dbh->prepare($sql) or die dbh->errstr; -  warn "FS::Report::Table::Monthly\n$sql\n" if $DEBUG; -  $sth->execute -    or die "Unexpected error executing statement $sql: ". $sth->errstr; -  $sth->fetchrow_arrayref->[0] || 0;  }  =back diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index c186aa26a..e28beb760 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -307,6 +307,7 @@ if($curuser->access_right('Financial reports')) {    %report_financial = (      'Sales, Credits and Receipts' => [ $fsurl.'graph/report_money_time.html', 'Sales, credits and receipts summary graph' ], +    'Daily Sales, Credits and Receipts' => [ $fsurl.'graph/report_money_time_daily.html', 'Sales, credits and receipts (broken down by day) summary graph' ],      'Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg.html', 'Sales report and graph (by agent, package class and/or date range)' ],      'Rated Call Sales Report' => [ $fsurl.'graph/report_cust_bill_pkg_detail.html', 'Sales report and graph (by agent, package class, usage class and/or date range)' ],      'Employee Commission Report' => [ $fsurl.'search/report_employee_commission.html', '' ], diff --git a/httemplate/graph/elements/monthly.html b/httemplate/graph/elements/monthly.html index de2b2e9d4..a451ea8ff 100644 --- a/httemplate/graph/elements/monthly.html +++ b/httemplate/graph/elements/monthly.html @@ -19,6 +19,7 @@ Example:      'links'           => \@links,      # or \%link, #opt      'link_fromparam'  => 'param_from', #defaults to 'begin'      'link_toparam'    => 'param_to',   #defaults to 'end' +    'daily'           => 1, # omit for monthly granularity      #optional, pulled from CGI params if not specified      'start_month'     => $smonth, @@ -26,6 +27,12 @@ Example:      'end_month'       => $emonth,      'end_year'        => $eyear, + +    #optional, pulled from CGI params if not specified,  +    #only if 'daily' option is given +    'start_day'       => $sday, +    'end_day'         => $eday, +      #optional      'agentnum'        => $agentnum,      'nototal'         => 1, @@ -42,8 +49,7 @@ Example:              'data'          => $data->{'data'},              'row_labels'    => $data->{'item_labels'},              'graph_labels'  => $opt{'graph_labels'} || $data->{'item_labels'}, -            'col_labels'    => [ map { my $m = $_; $m =~ s/^(\d+)\//$mon[$1-1] / ; $m } -                                 @{$data->{label}} ], +            'col_labels'    => $col_labels,              'axis_labels'   => $data->{label},              'colors'        => $data->{colors},              'links'         => \@links, @@ -83,28 +89,41 @@ $opt{'start_year'}  ||= $cgi->param('start_year'); # || 1899+$curyear;  $opt{'end_month'} ||= $cgi->param('end_month'); # || $curmon+1;  $opt{'end_year'}  ||= $cgi->param('end_year'); # || 1900+$curyear; -my $report = new FS::Report::Table::Monthly ( - -  'items'        => \@items, -  'params'       => $opt{'params'}, -  'item_labels'  => ( $cgi->param('_type') =~ /^(png)$/ -                        ? $opt{'graph_labels'} -                        : $opt{'labels'} -                    ), -  'colors'       => $opt{'colors'}, -  'links'        => $opt{'links'}, - -  'start_month'  => $opt{'start_month'}, -  'start_year'   => $opt{'start_year'}, -  'end_month'    => $opt{'end_month'}, -  'end_year'     => $opt{'end_year'}, - -  'agentnum'     => $opt{'agentnum'}, -  'remove_empty' => $opt{'remove_empty'}, -  'doublemonths' => $opt{'doublemonths'}, +if ( $opt{'daily'} ) { # daily granularity +    $opt{'start_day'} ||= $cgi->param('start_day'); +    $opt{'end_day'} ||= $cgi->param('end_day'); +} + +my %reportopts = ( +      'items'        => \@items, +      'params'       => $opt{'params'}, +      'item_labels'  => ( $cgi->param('_type') =~ /^(png)$/ +                            ? $opt{'graph_labels'} +                            : $opt{'labels'} +                        ), +      'colors'       => $opt{'colors'}, +      'links'        => $opt{'links'}, + +      'start_day'    => $opt{'start_day'}, +      'start_month'  => $opt{'start_month'}, +      'start_year'   => $opt{'start_year'}, +      'end_day'      => $opt{'end_day'}, +      'end_month'    => $opt{'end_month'}, +      'end_year'     => $opt{'end_year'}, +      'agentnum'     => $opt{'agentnum'}, +      'remove_empty' => $opt{'remove_empty'}, +      'doublemonths' => $opt{'doublemonths'},  ); + +my $report; +$report = new FS::Report::Table::Daily(%reportopts) if $opt{'daily'}; +$report = new FS::Report::Table::Monthly(%reportopts) unless $opt{'daily'};  my $data = $report->data; +my $col_labels = [ map { my $m = $_; $m =~ s/^(\d+)\//$mon[$1-1] / ; $m } +                             @{$data->{label}} ]; +$col_labels = $data->{label} if $opt{'daily'}; +  my @links;  foreach my $link (@{ $data->{'links'} }) {    my @speriod = @{$data->{'speriod'}}; diff --git a/httemplate/graph/money_time_daily.cgi b/httemplate/graph/money_time_daily.cgi new file mode 100644 index 000000000..4d16ff871 --- /dev/null +++ b/httemplate/graph/money_time_daily.cgi @@ -0,0 +1,111 @@ +<% include('elements/monthly.html', +                'title'        => $agentname. +                                  'Daily Sales, Credits and Receipts Summary', +                'items'        => \@items, +                'labels'       => \%label, +                'graph_labels' => \%graph_label, +                'colors'       => \%color, +                'links'        => \%link, +                'agentnum'     => $agentnum, +                'nototal'      => scalar($cgi->param('12mo')), +                'daily'        => 1, +                'start_day'    => $smday, +                'start_month'  => $smon+1, +                'start_year'   => $syear, +                'end_day'      => $emday, +                'end_month'    => $emon+1, +                'end_year'     => $eyear, +             ) +%> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +#XXX or virtual +my( $agentnum, $agent ) = ('', ''); +if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) { +  $agentnum = $1; +  $agent = qsearchs('agent', { 'agentnum' => $agentnum } ); +  die "agentnum $agentnum not found!" unless $agent; +} + +my $agentname = $agent ? $agent->agent.' ' : ''; + +my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi); +my ($ssec,$smin,$shour,$smday,$smon,$syear,$swday,$syday,$sisdst)  +    = localtime($beginning); +my ($esec,$emin,$ehour,$emday,$emon,$eyear,$ewday,$eyday,$eisdst)  +    = localtime($ending); + +my @items = qw( invoiced netsales +                credits  netcredits +                payments receipts +                refunds  netrefunds +                cashflow netcashflow +              ); +if ( $cgi->param('12mo') == 1 ) { +  @items = map $_.'_12mo', @items; +} + +my %label = ( +  'invoiced'    => 'Gross Sales', +  'netsales'    =>   'Net Sales', +  'credits'     => 'Gross Credits', +  'netcredits'  =>   'Net Credits', +  'payments'    => 'Gross Receipts', +  'receipts'    =>   'Net Receipts', +  'refunds'     => 'Gross Refunds', +  'netrefunds'  =>   'Net Refunds', +  'cashflow'    => 'Gross Cashflow', +  'netcashflow' =>   'Net Cashflow', +); + +my %graph_suffix = ( + 'invoiced'    => ' (invoiced)',  + 'netsales'    => ' (invoiced - applied credits)', + 'credits'     => ' (credited)', + 'netcredits'  => ' (applied credits)', + 'payments'    => ' (payments)', + 'receipts'    => ' (applied payments)', + 'refunds'     => ' (refunds)', + 'netrefunds'  => ' (applied refunds)', + 'cashflow'    => ' (payments - refunds)', + 'netcashflow' => ' (applied payments - applied refunds)', +); +my %graph_label = map { $_ => $label{$_}.$graph_suffix{$_} } keys %label; + +$label{$_.'_12mo'} = $label{$_}. " (prev 12 months)" +  foreach keys %label; + +$graph_label{$_.'_12mo'} = $graph_label{$_}. " (prev 12 months)" +  foreach keys %graph_label; + +my %color = ( +  'invoiced'    => '9999ff', #light blue +  'netsales'    => '0000cc', #blue +  'credits'     => 'ff9999', #light red +  'netcredits'  => 'cc0000', #red +  'payments'    => '99cc99', #light green +  'receipts'    => '00cc00', #green +  'refunds'     => 'ffcc99', #light orange +  'netrefunds'  => 'ff9900', #orange +  'cashflow'    => '99cc33', #light olive +  'netcashflow' => '339900', #olive +); +$color{$_.'_12mo'} = $color{$_} +  foreach keys %color; + +my %link = ( +  'invoiced'   => "${p}search/cust_bill.html?agentnum=$agentnum;", +  'netsales'   => "${p}search/cust_bill.html?agentnum=$agentnum;net=1;", +  'credits'    => "${p}search/cust_credit.html?agentnum=$agentnum;", +  'netcredits' => "${p}search/cust_credit_bill.html?agentnum=$agentnum;", +  'payments'   => "${p}search/cust_pay.html?magic=_date;agentnum=$agentnum;", +  'receipts'   => "${p}search/cust_bill_pay.html?agentnum=$agentnum;", +  'refunds'    => "${p}search/cust_refund.html?magic=_date;agentnum=$agentnum;", +  'netrefunds' => "${p}search/cust_credit_refund.html?agentnum=$agentnum;", +); +# XXX link 12mo? + +</%init> diff --git a/httemplate/graph/report_money_time_daily.html b/httemplate/graph/report_money_time_daily.html new file mode 100644 index 000000000..1e1b45d4e --- /dev/null +++ b/httemplate/graph/report_money_time_daily.html @@ -0,0 +1,26 @@ +<% include('/elements/header.html', 'Daily Sales, Credits and Receipts Summary' ) %> + +<FORM ACTION="money_time_daily.cgi" METHOD="GET"> + +<TABLE> + +<% include( '/elements/tr-input-beginning_ending.html' ) %> + +<% include('/elements/tr-select-agent.html', +             'label'         => 'For agent: ', +             'disable_empty' => 0, +          ) +%> + +</TABLE> + +<BR><INPUT TYPE="submit" VALUE="Display"> +</FORM> + +<% include('/elements/footer.html') %> +<%init> + +die "access denied" +  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports'); + +</%init> | 
