missing file for #13289
[freeside.git] / httemplate / search / unearned_detail.html
1 <& elements/search.html,
2   'title'       => emt("Unearned revenue - ".ucfirst($unearned_mode)),
3   'name'        => emt('line items'),
4   'query'       => $query,
5   'count_query' => $count_query,
6   'count_addl'  => [ $money_char. '%.2f total',
7                      $money_char. '%.2f unearned revenue' 
8                    ],
9   'header'      => [ map( {emt $_} 
10     'Description',
11     'Unearned', # depends on mode
12     'Recurring charge', #recur - usage
13     'Owed', #recur - usage - credits - payments
14     'Paid', #payments
15     'Payment date', #of last payment
16     'Credit date', #of last credit
17     'Charge start',
18     'Charge end',
19     'Invoice',
20     'Date'
21     ),
22     FS::UI::Web::cust_header(),
23   ],
24   'fields'      => [
25     #Description
26     sub { $_[0]->pkgnum > 0
27       ? $_[0]->get('pkg')      # possibly use override.pkg
28       : $_[0]->get('itemdesc') # but i think this correct
29     },
30     #Unearned
31     money_sub('unearned_revenue'),
32     #Recurring charge
33     money_sub('recur_no_usage'),
34     #Owed
35     money_sub('owed_no_usage'),
36     #Paid
37     money_sub('paid_no_usage'),
38     #Payment date
39     date_sub('last_pay'),
40     #Credit date
41     date_sub('last_credit'),
42     #Charge start
43     date_sub('sdate'),
44     #Charge end
45     date_sub('edate'),
46     #Invoice
47     'invnum',
48     #Invoice date
49     date_sub('_date'),
50     \&FS::UI::Web::cust_fields,
51   ],
52   'sort_fields' => [
53     'pkg',
54     # SQL expressions work as sort keys...
55     'unearned_revenue',
56     'recur_no_usage',
57     'owed_no_usage',
58     'paid_no_usage',
59     'last_pay',
60     'last_credit',
61     'sdate',
62     'edate',
63     'invnum',
64     '_date',
65   ],
66   'links'       => [
67     ('' x 9),
68     $ilink,
69     $ilink,
70     ( map { $_ ne 'Cust. Status' ? $clink : '' }
71       FS::UI::Web::cust_header()
72     ),
73   ],
74   'align' => 'lrrcrccrc'.  FS::UI::Web::cust_aligns(),
75   'color' => [ 
76     ('' x 11),
77     FS::UI::Web::cust_colors(),
78   ],
79   'style' => [ 
80     ('' x 11),
81     FS::UI::Web::cust_styles(),
82   ],
83 &>
84 <%init>
85
86 # Separated from cust_bill_pkg.cgi to simplify things.
87
88 die "access denied"
89   unless $FS::CurrentUser::CurrentUser->access_right('Financial reports');
90
91 my $conf = new FS::Conf;
92
93 my $unearned = '';
94 my $unearned_mode = '';
95 my $unearned_base = '';
96 my $unearned_sql = '';
97
98 my @select = ( 'cust_bill_pkg.*', 'cust_bill._date' );
99 my ($join_cust, $join_pkg ) = ('', '');
100
101 #here is the agent virtualization
102 my $agentnums_sql =
103   $FS::CurrentUser::CurrentUser->agentnums_sql( 'table' => 'cust_main' );
104
105 my @where = ( $agentnums_sql );
106
107 my($beginning, $ending) = FS::UI::Web::parse_beginning_ending($cgi);
108
109 if ( $cgi->param('status') =~ /^([a-z]+)$/ ) {
110   push @where, FS::cust_main->cust_status_sql . " = '$1'";
111 }
112
113 push @where, "cust_bill._date >= $beginning",
114              "cust_bill._date <= $ending";
115
116 if ( $cgi->param('agentnum') =~ /^(\d+)$/ ) {
117   push @where, "cust_main.agentnum = $1";
118 }
119
120 # no pkgclass, no taxclass, no tax location...
121
122 # unearned revenue mode
123 $cgi->param('date') =~ /^(\d+)$/
124   or die "date required";
125
126 $unearned = $1;
127 $unearned_mode = $cgi->param('mode');
128
129 push @where, "cust_bill_pkg.sdate < $unearned",
130              "cust_bill_pkg.edate > $unearned",
131              "cust_bill_pkg.recur != 0",
132              "part_pkg.freq != '0'";
133
134 if ( !$cgi->param('include_monthly') ) {
135   push @where,
136              "part_pkg.freq != '1'",
137              "part_pkg.freq NOT LIKE '%h'",
138              "part_pkg.freq NOT LIKE '%d'",
139              "part_pkg.freq NOT LIKE '%w'";
140 }
141
142 my @opt = (
143   $unearned, #before this date
144   '',        #after this date
145   setuprecur => 'recur',
146   no_usage => 1
147 );
148
149 my $charged = FS::cust_bill_pkg->charged_sql(@opt);
150 push @select, "($charged) AS recur_no_usage";
151
152 my $owed_sql = FS::cust_bill_pkg->owed_sql(@opt);
153 push @select, "($owed_sql) AS owed_no_usage";
154
155 my $paid_sql = FS::cust_bill_pkg->paid_sql(@opt);
156 push @select, "$paid_sql AS paid_no_usage";
157
158 if ( $unearned_mode eq 'paid' ) {
159   # then use the amount paid, minus usage charges
160   $unearned_base = $paid_sql;
161 }
162 else {
163   # use the amount billed, minus usage charges and credits
164   $unearned_base = "( $charged - " . 
165                     FS::cust_bill_pkg->credited_sql(@opt) . ' )';
166 }
167 # whatever we're using as the base, only show rows where it's positive
168 push @where, "$unearned_base > 0";
169
170 my $period = "CAST(cust_bill_pkg.edate - cust_bill_pkg.sdate AS REAL)";
171 my $elapsed = "GREATEST( $unearned - cust_bill_pkg.sdate, 0 )";
172 my $remaining = "(1 - $elapsed/$period)";
173
174 $unearned_sql = "CAST( $unearned_base * $remaining AS DECIMAL(10,2) )";
175 push @select, "$unearned_sql AS unearned_revenue";
176
177 # last payment/credit date
178 my %t = (pay => 'cust_bill_pay', credit => 'cust_credit_bill');
179 foreach my $x (qw(pay credit)) {
180   my $table = $t{$x};
181   my $link = $table.'_pkg';
182   my $pkey = dbdef->table($table)->primary_key;
183   my $last_date_sql = "SELECT MAX(_date) 
184   FROM $table JOIN $link USING ($pkey)
185   WHERE $link.billpkgnum = cust_bill_pkg.billpkgnum 
186   AND $table._date <= $unearned";
187   push @select, "($last_date_sql) AS last_$x";
188 }
189
190 #no itemdesc
191 #no tax report group kludge
192 #no tax exemption
193 #usage always excluded
194
195 # always 'nottax', not 'istax'
196 $join_cust =  '        JOIN cust_bill USING ( invnum )
197                   LEFT JOIN cust_main USING ( custnum ) ';
198
199 $join_pkg .=  ' LEFT JOIN cust_pkg USING ( pkgnum )
200                 LEFT JOIN part_pkg USING ( pkgpart )
201                 LEFT JOIN part_pkg AS override
202                   ON pkgpart_override = override.pkgpart ';
203
204 my $where = ' WHERE '. join(' AND ', @where);
205
206 my $count_query = "SELECT COUNT(DISTINCT billpkgnum), 
207   SUM( $unearned_base ), SUM( $unearned_sql )
208   FROM cust_bill_pkg $join_cust $join_pkg $where";
209
210 push @select, 'part_pkg.pkg',
211               'part_pkg.freq',
212               'cust_main.custnum',
213               FS::UI::Web::cust_sql_fields();
214
215 my $query = {
216   'table'     => 'cust_bill_pkg',
217   'addl_from' => "$join_cust $join_pkg",
218   'hashref'   => {},
219   'select'    => join(",\n", @select ),
220   'extra_sql' => $where,
221   'order_by'  => 'ORDER BY cust_bill._date, billpkgnum',
222 };
223
224 my $ilink = [ "${p}view/cust_bill.cgi?", 'invnum' ];
225 my $clink = [ "${p}view/cust_main.cgi?", 'custnum' ];
226
227 my $money_char;
228
229 sub money_sub {
230   $conf ||= new FS::Conf;
231   $money_char ||= $conf->config('money_char') || '$';
232   my $field = shift;
233   sub {
234     $money_char . sprintf('%.2f', $_[0]->get($field));
235   };
236 }
237
238 sub date_sub {
239   my $field = shift;
240   sub {
241     my $value = $_[0]->get($field);
242     $value ? time2str('%b %d %Y', $value) : '';
243   };
244 };
245
246 </%init>