discount reporting, RT#6679
authorivan <ivan>
Sat, 6 Feb 2010 02:57:12 +0000 (02:57 +0000)
committerivan <ivan>
Sat, 6 Feb 2010 02:57:12 +0000 (02:57 +0000)
18 files changed:
FS/FS.pm
FS/FS/Mason.pm
FS/FS/Report/Table/Monthly.pm
FS/FS/Schema.pm
FS/FS/cust_bill_pkg.pm
FS/FS/cust_bill_pkg_discount.pm [new file with mode: 0644]
FS/FS/cust_main.pm
FS/FS/cust_pkg.pm
FS/FS/cust_pkg_discount.pm
FS/FS/part_pkg/flat.pm
FS/MANIFEST
FS/t/cust_bill_pkg_discount.t [new file with mode: 0644]
httemplate/elements/menu.html
httemplate/misc/delete-cust_pkg_discount.html
httemplate/search/cust_bill_pkg_discount.html [new file with mode: 0644]
httemplate/search/cust_pkg_discount.html [new file with mode: 0644]
httemplate/search/report_cust_bill_pkg_discount.html [new file with mode: 0644]
httemplate/search/report_cust_pkg_discount.html [new file with mode: 0644]

index bfa23af..67c2cd2 100644 (file)
--- a/FS/FS.pm
+++ b/FS/FS.pm
@@ -228,6 +228,8 @@ L<FS::cust_pkg_detail> - Customer package details class
 
 L<FS:;cust_pkg_discount> - Customer package discount class
 
+L<FS:;cust_bill_pkg_discount> - Customer package discount line item application class
+
 L<FS:;discount> - Discount class
 
 L<FS::reason_type> - Reason type class
index d812e81..f20ea64 100644 (file)
@@ -228,6 +228,8 @@ if ( -e $addl_handler_use_file ) {
   use FS::contact;
   use FS::svc_pbx;
   use FS::discount;
+  use FS::cust_pkg_discount;
+  use FS::cust_bill_pkg_discount;
   # Sammath Naur
 
   if ( $FS::Mason::addl_handler_use ) {
index fa9949d..9e7a288 100644 (file)
@@ -417,6 +417,53 @@ sub cust_bill_pkg_detail {
   
 }
 
+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'); }
index ad094d7..19c2e8e 100644 (file)
@@ -1317,12 +1317,26 @@ sub tables_hashref {
         'months_used',   'decimal', 'NULL', '', '', '',
         'end_date',     @date_type,             '', '',
         'otaker',        'varchar', '',     32, '', '', 
+        'disabled',         'char', 'NULL',  1, '', '', 
       ],
       'primary_key' => 'pkgdiscountnum',
       'unique' => [],
       'index'  => [ [ 'pkgnum' ], [ 'discountnum' ] ],
     },
 
+    'cust_bill_pkg_discount' => {
+      'columns' => [
+        'billpkgdiscountnum', 'serial',     '', '', '', '',
+        'billpkgnum',            'int',     '', '', '', '', 
+        'pkgdiscountnum',        'int',     '', '', '', '', 
+        'amount',          @money_type,             '', '', 
+        'months',            'decimal', 'NULL', '', '', '',
+      ],
+      'primary_key' => 'billpkgdiscountnum',
+      'unique' => [],
+      'index' => [ [ 'billpkgnum' ], [ 'pkgdiscountnum' ] ],
+    },
+
     'discount' => {
       'columns' => [
         'discountnum', 'serial',     '',      '', '', '',
index cd049d1..cb070d7 100644 (file)
@@ -174,6 +174,17 @@ sub insert {
     }
   }
 
+  if ( $self->get('discounts') ) {
+    foreach my $cust_bill_pkg_discount ( @{$self->get('discounts')} ) {
+      $cust_bill_pkg_discount->billpkgnum($self->billpkgnum);
+      $error = $cust_bill_pkg_discount->insert;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return "error inserting cust_bill_pkg_discount: $error";
+      }
+    }
+  }
+
   if ( $self->_cust_tax_exempt_pkg ) {
     foreach my $cust_tax_exempt_pkg ( @{$self->_cust_tax_exempt_pkg} ) {
       $cust_tax_exempt_pkg->billpkgnum($self->billpkgnum);
@@ -763,10 +774,10 @@ sub cust_bill_pkg_display {
   my $type = $opt{type} if exists $opt{type};
   my @result;
 
-  if ( scalar( $self->get('display') ) ) {
+  if ( $self->get('display') ) {
     @result = grep { defined($type) ? ($type eq $_->type) : 1 }
               @{ $self->get('display') };
-  }else{
+  } else {
     my $hashref = { 'billpkgnum' => $self->billpkgnum };
     $hashref->{type} = $type if defined($type);
     
diff --git a/FS/FS/cust_bill_pkg_discount.pm b/FS/FS/cust_bill_pkg_discount.pm
new file mode 100644 (file)
index 0000000..e7dd5f2
--- /dev/null
@@ -0,0 +1,158 @@
+package FS::cust_bill_pkg_discount;
+
+use strict;
+use base qw( FS::cust_main_Mixin FS::Record );
+use FS::Record qw( qsearch qsearchs );
+use FS::cust_bill_pkg;
+use FS::cust_pkg_discount;
+
+=head1 NAME
+
+FS::cust_bill_pkg_discount - Object methods for cust_bill_pkg_discount records
+
+=head1 SYNOPSIS
+
+  use FS::cust_bill_pkg_discount;
+
+  $record = new FS::cust_bill_pkg_discount \%hash;
+  $record = new FS::cust_bill_pkg_discount { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::cust_bill_pkg_discount object represents the slice of a customer
+applied to a line item.  FS::cust_bill_pkg_discount inherits from
+FS::Record.  The following fields are currently supported:
+
+=over 4
+
+=item billpkgdiscountnum
+
+primary key
+
+=item billpkgnum
+
+Line item (see L<FS::cust_bill_pkg>)
+
+=item pkgdiscountnum
+
+Customer discount (see L<FS::cust_pkg_discount>)
+
+=item amount
+
+Amount discounted from the line itme.
+
+=item months
+
+Number of months of discount this represents.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record.  To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to.  You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'cust_bill_pkg_discount'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=item check
+
+Checks all fields to make sure this is a valid record.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('billpkgdiscountnum')
+    || $self->ut_foreign_key('billpkgnum', 'cust_bill_pkg', 'billpkgnum' )
+    || $self->ut_foreign_key('pkgdiscountnum', 'cust_pkg_discount', 'pkgdiscountnum' )
+    || $self->ut_money('amount')
+    || $self->ut_float('months')
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=item cust_bill_pkg
+
+Returns the associated line item (see L<FS::cust_bill_pkg>).
+
+=cut
+
+sub cust_bill_pkg {
+  my $self = shift;
+  qsearchs( 'cust_bill_pkg', { 'billpkgnum' => $self->billpkgnum } ) ;
+}
+
+=item cust_pkg_discount
+
+Returns the associated customer discount (see L<FS::cust_pkg_discount>).
+
+=cut
+
+sub cust_pkg_discount {
+  my $self = shift;
+  qsearchs( 'cust_pkg_discount', { 'pkgdiscountnum' => $self->pkgdiscountnum });
+}
+
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
index 590e32f..d4ce0fd 100644 (file)
@@ -3065,7 +3065,7 @@ sub _make_lines {
   my $old_cust_pkg = new FS::cust_pkg \%hash;
 
   my @details = ();
-
+  my @discounts = ();
   my $lineitems = 0;
 
   $cust_pkg->pkgpart($part_pkg->pkgpart);
@@ -3150,6 +3150,7 @@ sub _make_lines {
                               );
     my %param = ( 'precommit_hooks'     => $precommit_hooks,
                   'increment_next_bill' => $increment_next_bill,
+                  'discounts'           => \@discounts,
                 );
 
     my $method = $options{cancel} ? 'calc_cancel' : 'calc_recur';
@@ -3229,6 +3230,7 @@ sub _make_lines {
         'unitrecur' => $unitrecur,
         'quantity'  => $cust_pkg->quantity,
         'details'   => \@details,
+        'discounts' => \@discounts,
         'hidden'    => $part_pkg->hidden,
       };
 
index acc73df..c3734b5 100644 (file)
@@ -2388,10 +2388,7 @@ sub cust_pkg_discount {
 
 sub cust_pkg_discount_active {
   my $self = shift;
-  grep { my $d = $_->discount;
-         ! $d->months || $_->months_used < $d->months; # XXX also end date
-       }
-       $self->cust_pkg_discount;
+  grep { $_->status eq 'active' } $self->cust_pkg_discount;
 }
 
 =back
index 8dd00de..1b97bba 100644 (file)
@@ -1,7 +1,7 @@
 package FS::cust_pkg_discount;
 
 use strict;
-use base qw( FS::Record );
+use base qw( FS::cust_main_Mixin FS::Record );
 use FS::Record qw( dbh qsearchs ); # qsearch );
 use FS::cust_pkg;
 use FS::discount;
@@ -165,6 +165,7 @@ sub check {
     || $self->ut_float('months_used') #actually decimal, but this will do
     || $self->ut_numbern('end_date')
     || $self->ut_text('otaker')
+    || $self->ut_enum('disabled', [ '', 'Y' ] )
   ;
   return $error if $error;
 
@@ -207,6 +208,24 @@ sub increment_months_used {
   $self->replace();
 }
 
+=item status
+
+=cut
+
+sub status {
+  my $self = shift;
+  my $discount = $self->discount;
+
+  if ( $self->disabled ne 'Y' 
+       and ( ! $discount->months || $self->months_used < $discount->months )
+             #XXX also end date
+     ) {
+    'active';
+  } else {
+    'expired';
+  }
+}
+
 =back
 
 =head1 BUGS
index e5fc089..b825dde 100644 (file)
@@ -11,6 +11,7 @@ use List::Util qw(min); # max);
 use FS::UI::bytecount;
 use FS::Conf;
 use FS::part_pkg;
+use FS::cust_bill_pkg_discount;
 
 @ISA = qw(FS::part_pkg);
 
@@ -190,6 +191,16 @@ sub calc_discount {
      $amount *= $months;
      $amount = sprintf('%.2f', $amount);
 
+     next unless $amount > 0;
+
+     #record details in cust_bill_pkg_discount
+     my $cust_bill_pkg_discount = new FS::cust_bill_pkg_discount {
+       'pkgdiscountnum' => $cust_pkg_discount->pkgdiscountnum,
+       'amount'         => $amount,
+       'months'         => $months,
+     };
+     push @{ $param->{'discounts'} }, $cust_bill_pkg_discount;
+
      #add details on discount to invoice
      my $conf = new FS::Conf;
      my $money_char = $conf->config('money_char') || '$';  
index a92b9dd..7152345 100644 (file)
@@ -480,3 +480,5 @@ FS/discount.pm
 t/discount.t
 FS/cust_pkg_discount.pm
 t/cust_pkg_discount.t
+FS/cust_bill_pkg_discount.pm
+t/cust_bill_pkg_discount.t
diff --git a/FS/t/cust_bill_pkg_discount.t b/FS/t/cust_bill_pkg_discount.t
new file mode 100644 (file)
index 0000000..74923e1
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::cust_bill_pkg_discount;
+$loaded=1;
+print "ok 1\n";
index 5090abc..1694a59 100644 (file)
@@ -102,6 +102,13 @@ tie my %report_invoices, 'Tie::IxHash',
   'Advanced invoice reports' => [ $fsurl.'search/report_cust_bill.html', 'by agent, date range, etc.' ],
 ;
 
+tie my %report_discounts, 'Tie::IxHash',
+  'Discount graph'  => [ $fsurl. 'graph/report_cust_bill_pkg_discount.html', 'Discount overview per month' ],
+  'Discount detail' => [ $fsurl.'search/report_cust_bill_pkg_discount.html', 'Discount report (by employee and/or date range)' ],
+  #awful name
+  'Package discounts' => [ $fsurl.'search/report_cust_pkg_discount.html', 'Active/inactive discounts by package' ],
+;
+
 tie my %report_services, 'Tie::IxHash';
 if ( $curuser->access_right('Configuration') ) {
   $report_services{'Service definitions'} =  [ $fsurl.'browse/part_svc.cgi?orderby=active', 'Service definitions by number of active packages' ];
@@ -262,6 +269,8 @@ $report_menu{'Customers'}   = [ \%report_customers, 'Customer reports'  ]
   if $curuser->access_right('List customers');
 $report_menu{'Invoices'}    =  [ \%report_invoices,  'Invoice reports'   ]
   if $curuser->access_right('List invoices');
+$report_menu{'Discounts'}   =  [ \%report_discounts, 'Discount reports'  ]
+  if $curuser->access_right('Financial reports');
 $report_menu{'Payments'}    =  [ \%report_payments,  'Payment reports'   ]
   if $curuser->access_right('Financial reports');
 $report_menu{'Packages'}    =  [ \%report_packages,  'Package reports'   ]
index 69168af..0bdaa13 100644 (file)
@@ -25,6 +25,8 @@ my $cust_pkg_discount =
 my $cust_pkg = $cust_pkg_discount->cust_pkg;
 my $custnum = $cust_pkg->custnum;
 
-my $error = $cust_pkg_discount->delete;
+$cust_pkg_discount->disabled('Y');
+
+my $error = $cust_pkg_discount->replace;
 
 </%init>
diff --git a/httemplate/search/cust_bill_pkg_discount.html b/httemplate/search/cust_bill_pkg_discount.html
new file mode 100644 (file)
index 0000000..088b291
--- /dev/null
@@ -0,0 +1,151 @@
+<% include( 'elements/search.html',
+                 'title'       => 'Discounts',
+                 'name'        => 'discounts',
+                 'query'       => $query,
+                 'count_query' => $count_query,
+                 'count_addl'  => [ $money_char. '%.2f total', ],
+                 'header'      => [
+                   #'#',
+                   'Discount',
+                   'Amount',
+                   'Months',
+                   'Package',
+                   'Invoice',
+                   'Date',
+                   FS::UI::Web::cust_header(),
+                 ],
+                 'fields'      => [
+                   #'billpkgdiscountnum',
+                   sub { $_[0]->cust_pkg_discount->discount->description },
+                   sub { sprintf($money_char.'%.2f', shift->amount ) },
+                   sub { my $m = shift->months;
+                         $m =~ /\./ ? sprintf('%.2f', $m) : $m;
+                       },
+                   'pkg',#sub { $_[0]->cust_bill_pkg->cust_pkg->part_pkg->pkg },
+                   'invnum',
+                   sub { time2str('%b %d %Y', shift->_date ) },
+                   \&FS::UI::Web::cust_fields,
+                 ],
+                 'links'       => [
+                   #'',
+                   '', #link to customer discount???
+                   '',
+                   '',
+                   '',
+                   $ilink,
+                   $ilink,
+                   ( map { $_ ne 'Cust. Status' ? $clink : '' }
+                         FS::UI::Web::cust_header()
+                   ),
+                 ],
+                 #'align' => 'rlrrrc'.FS::UI::Web::cust_aligns(),
+                 'align' => 'lrrlrr'.FS::UI::Web::cust_aligns(),
+                 'color' => [ 
+                              #'',
+                              '',
+                              '',
+                              '',
+                              '',
+                              '',
+                              '',
+                              FS::UI::Web::cust_colors(),
+                            ],
+                 'style' => [ 
+                              #'',
+                              '',
+                              '',
+                              '',
+                              '',
+                              '',
+                              '',
+                              FS::UI::Web::cust_styles(),
+                            ],
+           )
+%>
+<%init>
+
+#a little false laziness below w/cust_bill_pkg.cgi
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+#here is the agent virtualization
+my $agentnums_sql =
+  $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my @where = ( $agentnums_sql );
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+push @where, "_date >= $beginning",
+             "_date <= $ending";
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+  push @where, "cust_main.agentnum = $1";
+}
+
+# #classnum
+# # not specified: all classes
+# # 0: empty class
+# # N: classnum
+# my $use_override = $cgi->param('use_override');
+# if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
+#   my $comparison = '';
+#   if ( $1 == 0 ) {
+#     $comparison = "IS NULL";
+#   } else {
+#     $comparison = "= $1";
+#   }
+# 
+#   if ( $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";
+#   }
+# }
+
+my $count_query = "SELECT COUNT(*), SUM(amount)";
+
+my $join_cust =  '      JOIN cust_bill_pkg USING ( billpkgnum )
+                        JOIN cust_bill USING ( invnum ) 
+                   LEFT JOIN cust_main USING ( custnum ) ';
+
+my $join_pkg = ' LEFT JOIN cust_pkg USING ( pkgnum )
+                 LEFT JOIN part_pkg USING ( pkgpart ) ';
+                 #LEFT JOIN part_pkg AS override
+                 #  ON pkgpart_override = override.pkgpart ';
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+$count_query .= " FROM cust_bill_pkg_discount $join_cust $join_pkg $where";
+
+my @select = (
+               'cust_bill_pkg_discount.*',
+               #'cust_bill_pkg.*',
+               'cust_bill.invnum',
+               'cust_bill._date',
+             );
+push @select, 'part_pkg.pkg';
+push @select, 'cust_main.custnum',
+              FS::UI::Web::cust_sql_fields();
+
+my $query = {
+  'table'     => 'cust_bill_pkg_discount',
+  'addl_from' => "$join_cust $join_pkg",
+  'hashref'   => {},
+  'select'    => join(', ', @select ),
+  'extra_sql' => $where,
+  'order_by'  => 'ORDER BY _date, billpkgdiscountnum',
+};
+
+my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+</%init>
diff --git a/httemplate/search/cust_pkg_discount.html b/httemplate/search/cust_pkg_discount.html
new file mode 100644 (file)
index 0000000..233345e
--- /dev/null
@@ -0,0 +1,122 @@
+<% include( 'elements/search.html',
+                  'title'       => 'Package discounts', 
+                  'name'        => 'discounts',
+                  'query'       => $query,
+                  'count_query' => $count_query,
+                  #'redirect'    => $link,
+                  'header'      => [ 'Status',
+                                     'Discount',
+                                     'Months used',
+                                     'Employee',
+                                     'Package',
+                                     FS::UI::Web::cust_header(
+                                     #  $cgi->param('cust_fields')
+                                     ),
+                                   ],
+                  'fields'      => [
+                                     sub { ucfirst( shift->status ) },
+                                     sub { shift->discount->description },
+                                     sub { my $m = shift->months_used;
+                                           $m =~ /\./ ? sprintf('%.2f',$m) : $m;
+                                         },
+                                     'otaker',
+                                     'pkg',
+                                     \&FS::UI::Web::cust_fields,
+                                   ],
+                  'links'       => [
+                                     '',
+                                     '',
+                                     '',
+                                     '',
+                                     '',
+                                     ( map { $_ ne 'Cust. Status' ? $clink : ''}
+                                           FS::UI::Web::cust_header()
+                                     ),
+                                   ],
+                  'align'       => 'clrll'. FS::UI::Web::cust_aligns(),
+                  'color'       => [ 
+                                     '',
+                                     '',
+                                     '',
+                                     '',
+                                     '',
+                                     FS::UI::Web::cust_colors(),
+                                   ],
+                 'style'        => [ 
+                                     '',
+                                     '',
+                                     '',
+                                     '',
+                                     '',
+                                     FS::UI::Web::cust_styles(),
+                                   ],
+           )
+%>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+#my $conf = new FS::Conf;
+
+#here is the agent virtualization
+my $agentnums_sql =
+  $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my @where = ( $agentnums_sql );
+
+#status
+if ( $cgi->param('status') eq 'active' ) {
+  push @where, "     (    cust_pkg_discount.disabled IS NULL
+                       OR cust_pkg_discount.disabled != 'Y'  )
+                 AND ( months IS NULL OR months_used < months ) ";
+                       #XXX also end date
+} elsif ( $cgi->param('status') eq 'expired' ) {
+  push @where, "    (      cust_pkg_discount.disabled IS NOT NULL
+                      AND  cust_pkg_discount.disabled = 'Y'       )
+                 OR ( months IS NOT NULL AND months_used >= months )
+               ";     #XXX also end date
+}
+
+#otaker
+if ( $cgi->param('otaker') && $cgi->param('otaker') =~ /^([\w\.\-]+)$/ ) {
+  push @where, "cust_pkg_discount.otaker = '$1'";
+}
+
+#agent
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+  push @where, "cust_main.agentnum = $1";
+}
+
+my $count_query = "SELECT COUNT(*), SUM(amount)";
+
+my $join = ' LEFT JOIN discount  USING ( discountnum )
+             LEFT JOIN cust_pkg  USING ( pkgnum )
+             LEFT JOIN part_pkg  USING ( pkgpart )
+             LEFT JOIN cust_main USING ( custnum ) ';
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+$count_query .= " FROM cust_pkg_discount $join $where";
+
+my @select = (
+               'cust_pkg_discount.*',
+               'part_pkg.pkg',
+             );
+push @select, 'cust_main.custnum',
+              FS::UI::Web::cust_sql_fields();
+
+my $query = {
+  'table'     => 'cust_pkg_discount',
+  'addl_from' => $join,
+  'hashref'   => {},
+  'select'    => join(', ', @select ),
+  'extra_sql' => $where,
+  'order_by'  => 'ORDER BY pkgdiscountnum',
+};
+
+my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
+
+my $conf = new FS::Conf;
+
+</%init>
diff --git a/httemplate/search/report_cust_bill_pkg_discount.html b/httemplate/search/report_cust_bill_pkg_discount.html
new file mode 100644 (file)
index 0000000..f1879d4
--- /dev/null
@@ -0,0 +1,47 @@
+<% include('/elements/header.html', 'Discount report' ) %>
+
+<FORM ACTION="cust_bill_pkg_discount.html" METHOD="GET">
+
+
+<TABLE>
+
+  <% include( '/elements/tr-select-otaker.html',
+                'label'   => 'Discounts by employee: ',
+                'otakers' => \@otakers,
+            )
+  %>
+
+  <% include( '/elements/tr-select-agent.html',
+                 'curr_value'    => scalar( $cgi->param('agentnum') ),
+                 'label'         => 'for agent: ',
+                 'disable_empty' => 0,
+             )
+  %>
+
+  <% include( '/elements/tr-input-beginning_ending.html' ) %>
+
+  <% include( '/elements/tr-input-lessthan_greaterthan.html',
+                'label' => 'Amount',
+               'field' => 'amount',
+            )
+  %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_pkg_discount")
+  or die dbh->errstr;
+$sth->execute or die $sth->errstr;
+my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+
+</%init>
diff --git a/httemplate/search/report_cust_pkg_discount.html b/httemplate/search/report_cust_pkg_discount.html
new file mode 100644 (file)
index 0000000..7ebd44f
--- /dev/null
@@ -0,0 +1,50 @@
+<% include('/elements/header.html', 'Package discount report' ) %>
+
+<FORM ACTION="cust_pkg_discount.html" METHOD="GET">
+
+
+<TABLE>
+
+  <TR>
+    <TD>Discount status</TD>
+    <TD>
+      <SELECT NAME="status">
+        <OPTION VALUE="active">Active
+        <OPTION VALUE="expired">Expired
+        <OPTION VALUE="">(all)
+      </SELECT>
+    </TD>
+  </TR>
+
+  <% include( '/elements/tr-select-otaker.html',
+                'label'   => 'Discounts by employee: ',
+                'otakers' => \@otakers,
+            )
+  %>
+
+  <% include( '/elements/tr-select-agent.html',
+                 'curr_value'    => scalar( $cgi->param('agentnum') ),
+                 'label'         => 'for agent: ',
+                 'disable_empty' => 0,
+             )
+  %>
+
+</TABLE>
+
+<BR>
+<INPUT TYPE="submit" VALUE="Get Report">
+
+</FORM>
+
+<% include('/elements/footer.html') %>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $sth = dbh->prepare("SELECT DISTINCT otaker FROM cust_pkg_discount")
+  or die dbh->errstr;
+$sth->execute or die $sth->errstr;
+my @otakers = map { $_->[0] } @{$sth->fetchall_arrayref};
+
+</%init>