43ebb282b188bd831640ba5dac6b653eca2247ec
[freeside.git] / httemplate / view / cust_main / packages.html
1 <STYLE TYPE="text/css">
2 td.package {
3   vertical-align: top;
4   border-width: 0;
5   border-style: solid;
6 }
7 table.package {
8   border: none;
9   padding: 0;
10   border-spacing: 0;
11   width: 100%;
12 }
13 table.usage {
14   border: 1px solid black;
15   margin: auto;
16   width: 60%;
17   border-spacing: 0px;
18 }
19 .shared > * {
20   background-color: #ffffaa;
21 }
22 .row0 { background-color: #eeeeee; }
23 .row1 { background-color: #ffffff; }
24
25 table.hiddenrows {
26   width: 80%;
27   margin-left: 100px;
28   border: 1px solid #7E0079;
29   background-color: #cccccc;
30 }
31
32 .hiddenrows td {
33   text-align: center;
34 }
35
36 .rolldown_button {
37   min-width: 80px;
38   margin-left: 100px;
39   min-height: 20px;
40   background-color: #efefef;
41   border: 1px solid #7e0079;
42   z-index: 1;
43   text-align: center;
44 }
45
46 </STYLE>
47
48 % # activate rolldown buttons for hidden package blocks
49 <SCRIPT TYPE="text/javascript">
50 function toggle_rolldown() {
51   var up_arrow = <% decode_entities('&#x2b06') |js_string %>;
52   var dn_arrow = <% decode_entities('&#x2b07') |js_string %>;
53   var pkgnum = this.id.replace('rolldown_', '');
54   var hidden = document.getElementById('cust_pkg'+pkgnum+'_block');
55   if (hidden.style.display == 'none') {
56     hidden.style.display = '';
57     this.textContent = this.textContent.replace(dn_arrow, up_arrow);
58   } else {
59     hidden.style.display = 'none';
60     this.textContent = this.textContent.replace(up_arrow, dn_arrow);
61   }
62 }
63 <&| /elements/onload.js &>
64 var el;
65 % if ( $cgi->param('fragment') =~ /^cust_pkg(\d+)$/ ) {
66 % # IE-specific hack, but also unhide the row if it's in a hidden block
67 el = document.getElementById('cust_pkg<% $1 %>');
68 % }
69 var all_buttons = document.getElementsByClassName('rolldown_button');
70 for (var i = 0; i < all_buttons.length; i++) {
71   all_buttons[i].onclick = toggle_rolldown;
72   var block_id = all_buttons[i].id.replace('rolldown_', '');
73   if ( el && document.getElementById('cust_pkg'+block_id+'_block')
74                      .contains(el)
75      ) {
76     // then toggle it now
77     all_buttons[i].click();
78   }
79 }
80 if ( el ) el.scrollIntoView(true);
81 </&>
82 </SCRIPT>
83
84 <TABLE>
85   <TR>
86     <TD ALIGN="left" VALIGN="top">
87
88 % my $br = 0;
89
90 % if ( $total_pkgs > $maxrecords ) {
91
92 %   if ( 1 ) { #FS::pkg_class->num_pkg_class ) {
93       <% $br++ ? ' | ' : '' %>
94       Class: <& /elements/select-cust-pkg_class.html,
95                   curr_value  => scalar($cgi->param('classnum')),
96                   onchange    => 'classnum_changed(this);',
97                   pre_options => [ '-1' => 'all',
98                                     '0' => '(none)',
99                                  ],
100              &>
101 %   }
102
103     <% $br++ ? ' | ' : '' %>
104     Status: <& /elements/select-cust_pkg-status.html,
105                  curr_value => scalar($cgi->param('status')),
106                  onchange   => 'status_changed(this);',
107             &>
108
109 %   if ( 1 ) { #$cust_main->num_cust_location ) {
110       <% $br++ ? ' | ' : '' %>
111       Location: <& /elements/select-cust_location.html,
112                      cust_main   => $cust_main,
113                      curr_value  => scalar($cgi->param('locationnum')),
114                      addnew      => 0,
115                      onchange    => 'locationnum_changed(this);',
116                      pre_options => [ '-1' => 'all', ],
117                 &>
118 %   }
119
120   <SCRIPT TYPE="text/javascript">
121
122     function classnum_changed(what) {
123 %     my $classnum = $cgi->param('classnum');
124 %     $cgi->delete('classnum');
125       window.location = '<% $cgi->self_url %>;classnum=' + what.options[what.selectedIndex].value;
126 %     $cgi->param('classnum', $classnum);
127     }
128
129     function status_changed(what) {
130 %     my $status = $cgi->param('status');
131 %     $cgi->delete('status');
132       window.location = '<% $cgi->self_url %>;status=' + what.options[what.selectedIndex].value;
133 %     $cgi->param('status', $status);
134     }
135
136     function locationnum_changed(what) {
137 %     my $locationnum = $cgi->param('locationnum');
138 %     $cgi->delete('locationnum');
139       window.location = '<% $cgi->self_url %>;locationnum=' + what.options[what.selectedIndex].value;
140 %     $cgi->param('locationnum', $locationnum);
141     }
142
143   </SCRIPT>
144
145 % }
146
147 <% $br++ ? ' | ' : '' %>
148 % if ( $cust_main->num_cancelled_pkgs ) {
149 %     if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me
150 %          || ( $conf->exists('hidecancelledpackages')
151 %               && ! $cgi->param('showcancelledpackages')
152 %             )
153 %        )
154 %     {
155 %       my $prev = $cgi->param('showcancelledpackages');
156 %       $cgi->param('showcancelledpackages', 1);
157   <a href="<% $cgi->self_url %>"><% mt('show') |h %> 
158 %       $cgi->param('showcancelledpackages', $prev);
159 %   } else {
160 %       $cgi->param('showcancelledpackages', 0);
161   <a href="<% $cgi->self_url %>"><% mt('hide') |h %> 
162 %       $cgi->param('showcancelledpackages', 1);
163 %   } 
164
165  <% mt('cancelled packages') |h %></a>
166 % } 
167
168 <% $br++ ? ' | ' : '' %>
169 % if ( $cgi->param('showoldpackages') ) {
170 %   $cgi->param('showoldpackages', 0);
171     <a href="<% $cgi->self_url %>"><% mt('hide old packages') |h %></a>
172 %   $cgi->param('showoldpackages', 1);
173 % } else {
174 %   $cgi->param('showoldpackages', 1);
175     <a href="<% $cgi->self_url %>"><% mt('show old packages') |h %></a>
176 %   $cgi->param('showoldpackages', 0);
177 % }
178
179     </TD>
180
181   </TR>
182
183 % my $pager = include('/elements/pager.html',
184 %                       offset     => $offset,
185 %                       num_rows   => scalar(@packages),
186 %                       total      => $num_pkgs,
187 %                       maxrecords => $maxrecords,
188 %             );
189 % if ( $num_pkgs > $maxrecords ) {
190   <TR>
191     <TD COLSPAN=2>
192       <% $pager %>
193     </TD>
194   </TR>
195 % }
196
197   <TR>
198     <TD COLSPAN=2>
199
200 %     $opt{cust_main} = $cust_main;
201 %     $opt{packages}  = \@packages;
202 %     $opt{cust_location_cache} = {};
203 %     if ( $conf->exists('cust_pkg-group_by_location') ) {
204         <& locations.html, %opt &>
205 %     } else { # in this format, put all packages in one section
206         <& /elements/table-grid.html &>
207         <& packages/section.html, %opt &>
208         </TABLE>
209 %     }
210
211     </TD>
212   </TR>
213
214 % if ( $num_pkgs > $maxrecords ) {
215   <TR>
216     <TD COLSPAN=2>
217       <% $pager %>
218     </TD>
219   </TR>
220 % }
221
222 </TABLE>
223 <%init>
224
225 my $cust_main = shift;
226 my %opt = @_;
227 my $conf = new FS::Conf;
228
229 my $curuser = $FS::CurrentUser::CurrentUser;
230
231 my $countrydefault = scalar($conf->config('countrydefault')) || 'US';
232
233 my $hide_cancelled = 0;
234 if (  $cgi->param('showcancelledpackages') eq '0' #see if it was set by me
235    || ( $conf->exists('hidecancelledpackages')
236          && ! $cgi->param('showcancelledpackages') )
237    )
238 {
239   $hide_cancelled = 1;
240 }
241
242 my $cust_pkg_fields =
243   join(', ', map { "cust_pkg.$_ AS $_"          } fields('cust_pkg') );
244
245 my $part_pkg_fields =
246   join(', ', ( map { "part_pkg.$_ AS part_pkg_$_" } fields('part_pkg') ),
247              'setup_option.optionvalue AS part_pkg__setup_fee',
248              'recur_option.optionvalue AS part_pkg__recur_fee',
249       );
250
251 my $group_by =
252   join(', ', map "cust_pkg.$_", fields('cust_pkg') ). ', '.
253   join(', ', map "part_pkg.$_", fields('part_pkg') );
254
255 my $num_svcs = '( SELECT COUNT(*) FROM cust_svc '.
256                '    WHERE cust_svc.pkgnum = cust_pkg.pkgnum ) AS num_svcs';
257
258 my $addl_from = '
259   LEFT JOIN part_pkg USING ( pkgpart )
260   LEFT JOIN cust_pkg AS chgto   ON ( chgto.change_to_pkgnum = cust_pkg.pkgnum )
261   LEFT JOIN cust_pkg AS chgfrom ON ( chgfrom.change_pkgnum  = cust_pkg.pkgnum )
262 ';
263
264 my $extra_sql =
265   ' AND cust_pkg.main_pkgnum IS NULL '. # supplemental package of something else
266   ' AND chgto.pkgnum IS NULL '. # ordered, not-yet-active change target
267   ' AND chgfrom.pkgnum IS NULL ';    # canceled package changed into another
268
269 unless ( $cgi->param('showoldpackages') ) {
270   my $years = $conf->config('cust_main-packages-years') || 2;
271   my $then = time - $years * 31556926; #60*60*24*365.2422 is close enough
272
273   $extra_sql .= " AND (
274        ( part_pkg.freq  =  '0'
275            AND ( cust_pkg.setup  IS NULL OR cust_pkg.setup  > $then )
276        )
277     OR ( part_pkg.freq != '0'
278           AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel > $then )
279        )
280     OR EXISTS ( SELECT 1 FROM cust_svc WHERE cust_svc.pkgnum = cust_pkg.pkgnum )
281   )";
282 }
283
284 if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
285   my $classnum = $1;
286   if ( $classnum == 0 ) {
287     $extra_sql .= " AND part_pkg.classnum IS NULL ";
288   } else {
289     $extra_sql .= " AND part_pkg.classnum = $classnum ";
290   }
291 }
292
293 if ( $cgi->param('status') =~ /^([\w ]+)$/ ) {
294   my $status = $1;
295   $extra_sql .= ' AND '. FS::cust_pkg->status_sql. " = '$status' ";
296 }
297
298 if ( $cgi->param('locationnum') =~ /^(\d+)$/ ) {
299   my $locationnum = $1;
300   $extra_sql .= " AND cust_pkg.locationnum = $locationnum ";
301 }
302
303 my $total_pkgs = $cust_main->all_pkgs;
304
305 my $method = $hide_cancelled ? 'ncancelled_pkgs' : 'all_pkgs';
306 my $num_pkgs = $cust_main->$method({ 
307   'addl_from' => $addl_from,
308   'extra_sql' => $extra_sql,
309 });
310
311 my $maxrecords = 10;
312 my $offset = $cgi->param('offset') =~ /^(\d+)$/ ? $1 : 0;
313 $cgi->delete('offset');
314
315 my @packages = $cust_main->$method( {
316   'select'    => "$cust_pkg_fields, $part_pkg_fields, $num_svcs",
317   'addl_from' => $addl_from. "
318     LEFT JOIN part_pkg_option AS setup_option
319       ON (     cust_pkg.pkgpart = setup_option.pkgpart
320            AND setup_option.optionname = 'setup_fee' )
321     LEFT JOIN part_pkg_option AS recur_option
322       ON (     cust_pkg.pkgpart = recur_option.pkgpart
323            AND recur_option.optionname = 'recur_fee' )
324   ",
325   'extra_sql' => $extra_sql,
326   'order_by'  => "ORDER BY pkgnum ASC LIMIT $maxrecords OFFSET $offset",
327 } );
328
329 foreach my $cust_pkg ( @packages ) {
330
331   my %hash = $cust_pkg->hash;
332   my %part_pkg = map  { /^part_pkg_(.+)$/ or die; ( $1 => $hash{$_} ); }
333                  grep { /^part_pkg_/ } keys %hash;
334   $cust_pkg->{'_pkgpart'} = new FS::part_pkg \%part_pkg;
335
336   #arrayref of supplementals
337   $cust_pkg->set('_supplemental', [
338     qsearch('cust_pkg', { main_pkgnum=>$cust_pkg->pkgnum })
339   ] );
340
341   #for future changes
342   if ( $cust_pkg->change_to_pkgnum ) {
343     my $change_to =
344       qsearchs('cust_pkg', { pkgnum=>$cust_pkg->change_to_pkgnum });
345     $cust_pkg->set('change_to_pkg', $change_to);
346     $change_to->set('change_from_pkg', $cust_pkg);
347   }
348
349   #for past changes
350   setfrom($cust_pkg);
351
352   $cust_pkg->{'_cust_pkg_discount_active'} =
353    [ $cust_pkg->cust_pkg_discount_active ];
354
355 }
356
357 sub setfrom {
358   my $cust_pkg = shift;
359
360   if ( $cust_pkg->change_pkgnum ) {
361     my $changed_from =
362       qsearchs('cust_pkg', { pkgnum=>$cust_pkg->change_pkgnum });
363     $cust_pkg->set('changed_from_pkg', $changed_from);
364     $changed_from->set('changed_to_pkg', $cust_pkg);
365
366     setfrom($changed_from);
367
368   }
369
370 }
371
372 </%init>