excel number format fix, #17971
[freeside.git] / httemplate / search / cust_bill_pkg_referral.html
1 <& elements/search.html,
2   'title'       => emt('Sales with advertising source'),
3   'name'        => emt('line items'),
4   'query'       => $query,
5   'count_query' => $count_query,
6   'count_addl'  => [ 
7                      ($setup ? $money_char. '%.2f setup' : ()),
8                      ($recur ? $money_char. '%.2f recurring' : ()),
9                      ($usage ? $money_char. '%.2f usage' : ()),
10                    ],
11   'header'      => [
12     emt('Description'),
13     ($setup ? emt('Setup') : ()),
14     ($recur ? emt('Recurring') : ()),
15     ($usage ? emt('Usage') : ()),
16     emt('Invoice'),
17     emt('Invoice date'),
18     emt('Paid'),
19     emt('Payment date'),
20     emt('Pkg. status'),
21     emt('Pkg. class'),
22     '', #report class
23     emt('Cust#'),
24     emt('Customer'),
25     emt('Ad source'),
26     emt('Agent'),
27   ],
28   'fields'      => [
29     'pkg',
30     ($setup ? money_sub('setup') : ()),
31     ($recur ? money_sub('recur_no_usage') : ()),
32     ($usage ? money_sub('recur_usage') : ()),
33     'invnum',
34     date_sub('_date'),
35     money_sub('paid'),
36     date_sub('last_pay'),
37     sub {
38       my $cust_pkg = shift->cust_pkg;
39       $cust_pkg ? ucfirst($cust_pkg->status) : '';
40     },
41     'classname',
42     sub { # report_option
43       my $cust_bill_pkg = shift;
44       my $pkgpart = $cust_bill_pkg->pkgpart_override
45                  || $cust_bill_pkg->cust_pkg->pkgpart;
46       if ( !exists($report_classes{$pkgpart}) ) {
47         my $part_pkg = FS::part_pkg->by_key($pkgpart);
48         my %opts = $part_pkg->options;
49         $report_classes{$pkgpart} = [
50           map { /^report_option_(\d+)/ ? 
51                 $report_option_name{$1} :
52                 () }
53           keys %opts
54         ];
55       }
56       join( '<BR>', @{ $report_classes{$pkgpart} });
57     },
58     'custnum',
59     'name',
60     'referral', # from query
61     'agent',
62   ],
63   'sort_fields' => [
64     '',
65     ($setup ? 'setup' : ()),
66     ($recur ? 'recur_no_usage' : ()),
67     ($usage ? 'recur_usage' : ()),
68     'invnum',
69     '_date',
70     'paid',
71     'last_pay',
72     '', #package status
73     'classname',
74     '', #report_option
75     'custnum',
76     '',
77     'referral',
78     'agent',
79   ],
80   'links'       => [
81     '', #package/item desc
82     ('') x $x, #setup/recur/usage
83     $ilink, #invnum
84     $ilink, #invoice date
85     '', #paid amt
86     '', #payment date
87     '', #pkg status
88     '', #classnum
89     '', #report class
90     $clink, #custnum
91     $clink, #customer name
92     '', #referral
93     '', #agent
94   ],
95   #'align' => 'rlrrrc'.FS::UI::Web::cust_aligns(),
96   'align' => 'l' . ('r' x $x) . 'rcrccccrlll',
97   'color' => [ ('') x (5 + $x),
98                 sub {
99                   my $cust_pkg = shift->cust_pkg;
100                   $cust_pkg ? ucfirst($cust_pkg->statuscolor) : '';
101                 },
102                ('') x 6,
103              ],
104   'style' => [
105                ('') x (5 + $x),
106                'b',
107                ('') x 6
108              ],
109 &>
110 <%init>
111
112 die "access denied"
113   unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
114
115 my $conf = new FS::Conf;
116
117 my $setup = $cgi->param('setup') ? 1 : 0;
118 my $recur = $cgi->param('recur') ? 1 : 0;
119 my $usage = $cgi->param('usage') ? 1 : 0;
120
121 my $x = $setup + $recur + $usage;
122
123 my @select = ( 'cust_bill_pkg.*', 'cust_bill._date' );
124 my ($join_cust, $join_pkg ) = ('', '');
125
126 #here is the agent virtualization
127 my $agentnums_sql =
128   $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
129
130 my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
131
132 my @where = ( $agentnums_sql,
133               'cust_bill_pkg.pkgnum != 0', # exclude taxes
134               "cust_bill._date >= $beginning",
135               "cust_bill._date <= $ending",
136             );
137
138 if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
139   push @where, FS::cust_pkg->cust_status_sql . " = '$1'";
140 }
141
142 if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
143   push @where, "cust_main.agentnum = $1";
144 }
145
146 #classnum
147 # not specified: all classes
148 # 0: empty class
149 # N: classnum
150 my $use_override = 1; #$cgi->param('use_override');
151 if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
152   my $comparison = '';
153   if ( $1 == 0 ) {
154     $comparison = "IS NULL";
155   } else {
156     $comparison = "= $1";
157   }
158
159   if ( $use_override ) {
160     push @where, "(
161       part_pkg.classnum $comparison AND pkgpart_override IS NULL OR
162       override.classnum $comparison AND pkgpart_override IS NOT NULL
163     )";
164   } else {
165     push @where, "part_pkg.classnum $comparison";
166   }
167 }
168
169 # report option
170 my @report_option = grep /^\d+$/, ( $cgi->param('report_option') );
171 if ( @report_option ) {
172   @report_option = map { "'report_option_$_'" } @report_option;
173   push @where, "EXISTS( 
174     SELECT 1 FROM part_pkg_option WHERE optionname IN (".
175     join(',', @report_option).") AND (
176       part_pkg_option.pkgpart = cust_pkg.pkgpart AND pkgpart_override IS NULL
177       OR part_pkg_option.pkgpart = pkgpart_override
178     )
179   )";
180 }
181
182 my $setup_sql =
183   FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'setup');
184 my $recur_sql =
185   FS::cust_bill_pkg->charged_sql('', '', setuprecur => 'recur', no_usage => 1);
186 my $usage_sql = FS::cust_bill_pkg->usage_sql;
187
188 # exclude zero-amount items
189 my @orwhere;
190 push @orwhere, "(cust_bill_pkg.setup > 0)" if $setup;
191 push @orwhere, "($recur_sql > 0)"          if $recur;
192 push @orwhere, "($usage_sql > 0)"          if $usage;
193 push @where, '('.join(' OR ', @orwhere).')' if @orwhere;
194
195 $join_cust =  '        JOIN cust_bill     USING ( invnum )
196                   LEFT JOIN cust_main     USING ( custnum )
197                   LEFT JOIN part_referral USING ( refnum )
198                   LEFT JOIN agent ON cust_main.agentnum = agent.agentnum
199               ';
200
201 $join_pkg .=  ' LEFT JOIN cust_pkg USING ( pkgnum )
202                 LEFT JOIN part_pkg USING ( pkgpart )
203                 LEFT JOIN part_pkg AS override
204                   ON pkgpart_override = override.pkgpart 
205                 LEFT JOIN pkg_class ON '; #...
206
207 if ( $use_override ) {
208   # join to whichever pkgpart is appropriate
209   $join_pkg .= '
210       ( pkgpart_override IS NULL     AND part_pkg.classnum = pkg_class.classnum )
211    OR ( pkgpart_override IS NOT NULL AND override.classnum = pkg_class.classnum )';
212 } else {
213   $join_pkg .= 'part_pkg.classnum = pkg_class.classnum';
214 }
215
216 my $where = ' WHERE '. join(' AND ', @where);
217
218 # setup and recurring only
219 my $count_query = "SELECT 
220   COUNT(billpkgnum)".
221   ($setup ? ", SUM($setup_sql)" : '').
222   ($recur ? ", SUM($recur_sql)" : '').
223   ($usage ? ", SUM($usage_sql)" : '').
224   " FROM cust_bill_pkg
225   $join_cust
226   $join_pkg
227   $where
228   ";
229
230 my $paid_sql = FS::cust_bill_pkg->paid_sql('', '');
231 my $last_pay_sql = "SELECT MAX(_date)
232   FROM cust_bill_pay JOIN cust_bill_pay_pkg USING (billpaynum)
233   WHERE cust_bill_pay_pkg.billpkgnum = cust_bill_pkg.billpkgnum";
234
235 push @select, 'part_pkg.pkg',
236               'part_pkg.freq',
237               'cust_main.custnum',
238               'cust_main.first',
239               'cust_main.last',
240               'cust_main.company',
241               'part_referral.referral',
242               "($paid_sql) AS paid",
243               "($last_pay_sql) AS last_pay",
244               "($recur_sql) AS recur_no_usage",
245               "($usage_sql) AS recur_usage",
246               'pkg_class.classname',
247               'agent.agent',
248               ;
249
250 my $query = {
251   'table'     => 'cust_bill_pkg',
252   'addl_from' => "$join_cust $join_pkg",
253   'hashref'   => {},
254   'select'    => join(",\n", @select ),
255   'extra_sql' => $where,
256   'order_by'  => 'ORDER BY cust_bill._date, billpkgnum',
257 };
258
259 my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
260 my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
261
262 my $conf = new FS::Conf;
263 my $money_char = $conf->config('money_char') || '$';
264
265 my %report_classes; #cache
266 my %report_option_name = 
267   map { $_->num => $_->name } qsearch('part_pkg_report_option', {});
268
269 # should this be in Mason.pm or something?
270 sub money_sub {
271   $conf ||= new FS::Conf;
272   $money_char ||= $conf->config('money_char') || '$';
273   my $field = shift;
274   sub {
275     $money_char . sprintf('%.2f', $_[0]->get($field));
276   };
277 }
278
279 sub date_sub {
280   my $field = shift;
281   sub {
282     my $value = $_[0]->get($field);
283     $value ? time2str('%b %d %Y', $value) : '';
284   };
285 }
286
287 </%init>