[freeside-commits] branch FREESIDE_2_3_BRANCH updated. 2eac549e2daf3114eed36fc6bc3c86c954c6bba1

Mark Wells mark at 420.am
Fri Jul 6 12:28:53 PDT 2012


The branch, FREESIDE_2_3_BRANCH has been updated
       via  2eac549e2daf3114eed36fc6bc3c86c954c6bba1 (commit)
      from  2651b04dec3f0ef42e8a125cdae5a1b44691af5c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 2eac549e2daf3114eed36fc6bc3c86c954c6bba1
Author: Mark Wells <mark at freeside.biz>
Date:   Fri Jul 6 12:25:19 2012 -0700

    sales by ad source report, #17971

diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 0f85236..cca6bb1 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -313,6 +313,7 @@ if($curuser->access_right('Financial reports')) {
     '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)' ],
+    'Sales With Advertising Source' => [ $fsurl.'search/report_cust_bill_pkg_referral.html' ],
     'Employee Commission Report' => [ $fsurl.'search/report_employee_commission.html', '' ],
     'Credit Report' => [ $fsurl.'search/report_cust_credit.html', 'Credit report (by employee and/or date range)' ],
     'Unapplied Credits' => [ $fsurl.'search/report_cust_credit.html?unapplied=1', 'Unapplied credit report (by type and/or date range)' ],
diff --git a/httemplate/search/cust_bill_pkg_referral.html b/httemplate/search/cust_bill_pkg_referral.html
new file mode 100644
index 0000000..f17cea3
--- /dev/null
+++ b/httemplate/search/cust_bill_pkg_referral.html
@@ -0,0 +1,287 @@
+<& elements/search.html,
+  'title'       => emt('Sales with advertising source'),
+  'name'        => emt('line items'),
+  'query'       => $query,
+  'count_query' => $count_query,
+  'count_addl'  => [ 
+                     ($setup ? $money_char. '%.2f setup' : ()),
+                     ($recur ? $money_char. '%.2f recurring' : ()),
+                     ($usage ? $money_char. '%.2f usage' : ()),
+                   ],
+  'header'      => [
+    emt('Description'),
+    ($setup ? emt('Setup') : ()),
+    ($recur ? emt('Recurring') : ()),
+    ($usage ? emt('Usage') : ()),
+    emt('Invoice'),
+    emt('Invoice date'),
+    emt('Paid'),
+    emt('Payment date'),
+    emt('Pkg. status'),
+    emt('Pkg. class'),
+    '', #report class
+    emt('Cust#'),
+    emt('Customer'),
+    emt('Ad source'),
+    emt('Agent'),
+  ],
+  'fields'      => [
+    'pkg',
+    ($setup ? money_sub('setup') : ()),
+    ($recur ? money_sub('recur_no_usage') : ()),
+    ($usage ? money_sub('recur_usage') : ()),
+    'invnum',
+    date_sub('_date'),
+    money_sub('paid'),
+    date_sub('last_pay'),
+    sub {
+      my $cust_pkg = shift->cust_pkg;
+      $cust_pkg ? ucfirst($cust_pkg->status) : '';
+    },
+    'classname',
+    sub { # report_option
+      my $cust_bill_pkg = shift;
+      my $pkgpart = $cust_bill_pkg->pkgpart_override
+                 || $cust_bill_pkg->cust_pkg->pkgpart;
+      if ( !exists($report_classes{$pkgpart}) ) {
+        my $part_pkg = FS::part_pkg->by_key($pkgpart);
+        my %opts = $part_pkg->options;
+        $report_classes{$pkgpart} = [
+          map { /^report_option_(\d+)/ ? 
+                $report_option_name{$1} :
+                () }
+          keys %opts
+        ];
+      }
+      join( '<BR>', @{ $report_classes{$pkgpart} });
+    },
+    'custnum',
+    'name',
+    'referral', # from query
+    'agent',
+  ],
+  'sort_fields' => [
+    '',
+    ($setup ? 'setup' : ()),
+    ($recur ? 'recur_no_usage' : ()),
+    ($usage ? 'recur_usage' : ()),
+    'invnum',
+    '_date',
+    'paid',
+    'last_pay',
+    '', #package status
+    'classname',
+    '', #report_option
+    'custnum',
+    '',
+    'referral',
+    'agent',
+  ],
+  'links'       => [
+    '', #package/item desc
+    ('') x $x, #setup/recur/usage
+    $ilink, #invnum
+    $ilink, #invoice date
+    '', #paid amt
+    '', #payment date
+    '', #pkg status
+    '', #classnum
+    '', #report class
+    $clink, #custnum
+    $clink, #customer name
+    '', #referral
+    '', #agent
+  ],
+  #'align' => 'rlrrrc'.FS::UI::Web::cust_aligns(),
+  'align' => 'l' . ('r' x $x) . 'rcrccccrlll',
+  'color' => [ ('') x (5 + $x),
+                sub {
+                  my $cust_pkg = shift->cust_pkg;
+                  $cust_pkg ? ucfirst($cust_pkg->statuscolor) : '';
+                },
+               ('') x 6,
+             ],
+  'style' => [
+               ('') x (5 + $x),
+               'b',
+               ('') x 6
+             ],
+&>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
+
+my $conf = new FS::Conf;
+
+my $setup = $cgi->param('setup') ? 1 : 0;
+my $recur = $cgi->param('recur') ? 1 : 0;
+my $usage = $cgi->param('usage') ? 1 : 0;
+
+my $x = $setup + $recur + $usage;
+
+my @select = ( 'cust_bill_pkg.*', 'cust_bill._date' );
+my ($join_cust, $join_pkg ) = ('', '');
+
+#here is the agent virtualization
+my $agentnums_sql =
+  $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
+
+my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
+
+my @where = ( $agentnums_sql,
+              'cust_bill_pkg.pkgnum != 0', # exclude taxes
+              "cust_bill._date >= $beginning",
+              "cust_bill._date <= $ending",
+            );
+
+if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
+  push @where, FS::cust_pkg->cust_status_sql . " = '$1'";
+}
+
+if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
+  push @where, "cust_main.agentnum = $1";
+}
+
+#classnum
+# not specified: all classes
+# 0: empty class
+# N: classnum
+my $use_override = 1; #$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";
+  }
+}
+
+# report option
+my @report_option = grep /^\d+$/, ( $cgi->param('report_option') );
+if ( @report_option ) {
+  @report_option = map { "'report_option_$_'" } @report_option;
+  push @where, "EXISTS( 
+    SELECT 1 FROM part_pkg_option WHERE optionname IN (".
+    join(',', @report_option).") AND (
+      part_pkg_option.pkgpart = cust_pkg.pkgpart AND pkgpart_override IS NULL
+      OR part_pkg_option.pkgpart = pkgpart_override
+    )
+  )";
+}
+
+my $setup_sql =
+  FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'setup');
+my $recur_sql =
+  FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'recur', no_usage => 1);
+my $usage_sql = FS::cust_bill_pkg->usage_sql;
+
+# exclude zero-amount items
+my @orwhere;
+push @orwhere, "(cust_bill_pkg.setup > 0)" if $setup;
+push @orwhere, "($recur_sql > 0)"          if $recur;
+push @orwhere, "($usage_sql > 0)"          if $usage;
+push @where, join(' OR ', @orwhere);
+
+$join_cust =  '        JOIN cust_bill     USING ( invnum )
+                  LEFT JOIN cust_main     USING ( custnum )
+                  LEFT JOIN part_referral USING ( refnum )
+                  LEFT JOIN agent ON cust_main.agentnum = agent.agentnum
+              ';
+
+$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 
+                LEFT JOIN pkg_class ON '; #...
+
+if ( $use_override ) {
+  # join to whichever pkgpart is appropriate
+  $join_pkg .= '
+      ( pkgpart_override IS NULL     AND part_pkg.classnum = pkg_class.classnum )
+   OR ( pkgpart_override IS NOT NULL AND override.classnum = pkg_class.classnum )';
+} else {
+  $join_pkg .= 'part_pkg.classnum = pkg_class.classnum';
+}
+
+my $where = ' WHERE '. join(' AND ', @where);
+
+# setup and recurring only
+my $count_query = "SELECT 
+  COUNT(billpkgnum)".
+  ($setup ? ", SUM($setup_sql)" : '').
+  ($recur ? ", SUM($recur_sql)" : '').
+  ($usage ? ", SUM($usage_sql)" : '').
+  " FROM cust_bill_pkg
+  $join_cust
+  $join_pkg
+  $where
+  ";
+
+my $paid_sql = FS::cust_bill_pkg->paid_sql('', '');
+my $last_pay_sql = "SELECT MAX(_date)
+  FROM cust_bill_pay JOIN cust_bill_pay_pkg USING (billpaynum)
+  WHERE cust_bill_pay_pkg.billpkgnum = cust_bill_pkg.billpkgnum";
+
+push @select, 'part_pkg.pkg',
+              'part_pkg.freq',
+              'cust_main.custnum',
+              'cust_main.first',
+              'cust_main.last',
+              'cust_main.company',
+              'part_referral.referral',
+              "($paid_sql) AS paid",
+              "($last_pay_sql) AS last_pay",
+              "($recur_sql) AS recur_no_usage",
+              "($usage_sql) AS recur_usage",
+              'pkg_class.classname',
+              'agent.agent',
+              ;
+
+my $query = {
+  'table'     => 'cust_bill_pkg',
+  'addl_from' => "$join_cust $join_pkg",
+  'hashref'   => {},
+  'select'    => join(",\n", @select ),
+  'extra_sql' => $where,
+  'order_by'  => 'ORDER BY cust_bill._date, billpkgnum',
+};
+
+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') || '$';
+
+my %report_classes; #cache
+my %report_option_name = 
+  map { $_->num => $_->name } qsearch('part_pkg_report_option', {});
+
+# should this be in Mason.pm or something?
+sub money_sub {
+  $conf ||= new FS::Conf;
+  $money_char ||= $conf->config('money_char') || '$';
+  my $field = shift;
+  sub {
+    $money_char . sprintf('%.2f', $_[0]->get($field));
+  };
+}
+
+sub date_sub {
+  my $field = shift;
+  sub {
+    my $value = $_[0]->get($field);
+    $value ? time2str('%b %d %Y', $value) : '';
+  };
+}
+
+</%init>
diff --git a/httemplate/search/elements/search-xls.html b/httemplate/search/elements/search-xls.html
index 0b5636c..6a19cf2 100644
--- a/httemplate/search/elements/search-xls.html
+++ b/httemplate/search/elements/search-xls.html
@@ -42,6 +42,9 @@ my $default_format = $workbook->add_format(locked => 0);
 my %money_format;
 my $money_char = FS::Conf->new->config('money_char') || '$';
 
+my %date_format;
+xl_parse_date_init();
+
 my $writer = sub {
   # Wrapper for $worksheet->write.
   # Do any massaging of the value/format here.
@@ -50,6 +53,7 @@ my $writer = sub {
     # Currency: strip the symbol, clone the requested format,
     # and format it for currency
     $value = $1;
+#    warn "formatting $value as money\n";
     if ( !exists($money_format{$format}) ) {
       $money_format{$format} = $workbook->add_format();
       $money_format{$format}->copy($format);
@@ -57,6 +61,22 @@ my $writer = sub {
     }
     $format = $money_format{$format};
   }
+  elsif ( $value =~ /^([A-Z][a-z]{2}) (\d{2}) (\d{4})$/ ) {
+    # Date: convert the value to an Excel date number and set 
+    # the format
+    $value = xl_parse_date($value);
+#    warn "formatting $value as date\n";
+    if ( !exists($date_format{$format}) ) {
+      $date_format{$format} = $workbook->add_format();
+      $date_format{$format}->copy($format);
+      $date_format{$format}->set_num_format('mmm dd yyyy');
+    }
+    $format = $date_format{$format};
+  }
+  else {
+    # String: replace line breaks with newlines
+    $value =~ s/<BR>/\n/gi;
+  }
   $worksheet->write($r, $c, $value, $format);
 };
 
diff --git a/httemplate/search/report_cust_bill_pkg_referral.html b/httemplate/search/report_cust_bill_pkg_referral.html
new file mode 100644
index 0000000..1fbb13d
--- /dev/null
+++ b/httemplate/search/report_cust_bill_pkg_referral.html
@@ -0,0 +1,55 @@
+<% include('/elements/header.html', 'Sales Report with Advertising Source' ) %>
+
+<FORM ACTION="cust_bill_pkg_referral.html" METHOD="GET">
+
+<TABLE>
+
+<& /elements/tr-input-beginning_ending.html &>
+
+<& /elements/tr-select-agent.html,
+  'label'         => 'For agent: ',
+  'disable_empty' => 0,
+  'empty_label'   => 'all',
+&>
+
+<& /elements/tr-select-pkg_class.html,
+  'pre_options' => [ '' => 'all', '0' => '(empty class)' ],
+  'disable_empty' => 1,
+&>
+
+<& /elements/tr-select-table.html,
+  'label'         => 'Report classes',
+  'table'         => 'part_pkg_report_option',
+  'name_col'      => 'name',
+  'hashref'       => { disabled => '' },
+  'element_name'  => 'report_option',
+  'multiple'      => 1,
+&>
+
+<TR>
+  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="setup" VALUE="1" CHECKED></TD>
+  <TD>Show setup/one-time fees</TD>
+</TR>
+
+<TR>
+  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="recur" VALUE="1" CHECKED></TD>
+  <TD>Show recurring fees</TD>
+</TR>
+
+<TR>
+  <TD ALIGN="right"><INPUT TYPE="checkbox" NAME="usage" VALUE="1" CHECKED></TD>
+  <TD>Show usage charges</TD>
+</TR>
+
+</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>

-----------------------------------------------------------------------

Summary of changes:
 httemplate/elements/menu.html                      |    1 +
 httemplate/search/cust_bill_pkg_referral.html      |  287 ++++++++++++++++++++
 httemplate/search/elements/search-xls.html         |   20 ++
 .../search/report_cust_bill_pkg_referral.html      |   55 ++++
 4 files changed, 363 insertions(+), 0 deletions(-)
 create mode 100644 httemplate/search/cust_bill_pkg_referral.html
 create mode 100644 httemplate/search/report_cust_bill_pkg_referral.html




More information about the freeside-commits mailing list