1 <STYLE TYPE="text/css">
14 border: 1px solid black;
20 background-color: #ffffaa;
22 .row0 { background-color: #eeeeee; }
23 .row1 { background-color: #ffffff; }
28 border: 1px solid #7E0079;
29 background-color: #cccccc;
40 background-color: #efefef;
41 border: 1px solid #7e0079;
48 % # activate rolldown buttons for hidden package blocks
49 <SCRIPT TYPE="text/javascript">
50 function toggle_rolldown() {
51 var up_arrow = <% decode_entities('⬆') |js_string %>;
52 var dn_arrow = <% decode_entities('⬇') |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);
59 hidden.style.display = 'none';
60 this.textContent = this.textContent.replace(up_arrow, dn_arrow);
63 <&| /elements/onload.js &>
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 %>');
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')
77 all_buttons[i].click();
80 if ( el ) el.scrollIntoView(true);
86 <TD ALIGN="left" VALIGN="top">
90 % if ( $total_pkgs > $maxrecords ) {
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',
103 <% $br++ ? ' | ' : '' %>
104 Status: <& /elements/select-cust_pkg-status.html,
105 curr_value => scalar($cgi->param('status')),
106 onchange => 'status_changed(this);',
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')),
115 onchange => 'locationnum_changed(this);',
116 pre_options => [ '-1' => 'all', ],
120 <SCRIPT TYPE="text/javascript">
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);
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);
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);
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')
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);
160 % $cgi->param('showcancelledpackages', 0);
161 <a href="<% $cgi->self_url %>"><% mt('hide') |h %>
162 % $cgi->param('showcancelledpackages', 1);
165 <% mt('cancelled packages') |h %></a>
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);
174 % $cgi->param('showoldpackages', 1);
175 <a href="<% $cgi->self_url %>"><% mt('show old packages') |h %></a>
176 % $cgi->param('showoldpackages', 0);
183 % my $pager = include('/elements/pager.html',
185 % num_rows => scalar(@packages),
186 % total => $num_pkgs,
187 % maxrecords => $maxrecords,
189 % if ( $num_pkgs > $maxrecords ) {
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 &>
214 % if ( $num_pkgs > $maxrecords ) {
225 my $cust_main = shift;
227 my $conf = new FS::Conf;
229 my $curuser = $FS::CurrentUser::CurrentUser;
231 my $countrydefault = scalar($conf->config('countrydefault')) || 'US';
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') )
242 my $cust_pkg_fields =
243 join(', ', map { "cust_pkg.$_ AS $_" } fields('cust_pkg') );
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',
252 join(', ', map "cust_pkg.$_", fields('cust_pkg') ). ', '.
253 join(', ', map "part_pkg.$_", fields('part_pkg') );
255 my $num_svcs = '( SELECT COUNT(*) FROM cust_svc '.
256 ' WHERE cust_svc.pkgnum = cust_pkg.pkgnum ) AS num_svcs';
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 )
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
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
273 $extra_sql .= " AND (
274 ( part_pkg.freq = '0'
275 AND ( cust_pkg.setup IS NULL OR cust_pkg.setup > $then )
277 OR ( part_pkg.freq != '0'
278 AND ( cust_pkg.cancel IS NULL OR cust_pkg.cancel > $then )
280 OR EXISTS ( SELECT 1 FROM cust_svc WHERE cust_svc.pkgnum = cust_pkg.pkgnum )
284 if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
286 if ( $classnum == 0 ) {
287 $extra_sql .= " AND part_pkg.classnum IS NULL ";
289 $extra_sql .= " AND part_pkg.classnum = $classnum ";
293 if ( $cgi->param('status') =~ /^([\w ]+)$/ ) {
295 $extra_sql .= ' AND '. FS::cust_pkg->status_sql. " = '$status' ";
298 if ( $cgi->param('locationnum') =~ /^(\d+)$/ ) {
299 my $locationnum = $1;
300 $extra_sql .= " AND cust_pkg.locationnum = $locationnum ";
303 my $total_pkgs = $cust_main->all_pkgs;
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,
312 my $offset = $cgi->param('offset') =~ /^(\d+)$/ ? $1 : 0;
313 $cgi->delete('offset');
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' )
325 'extra_sql' => $extra_sql,
326 'order_by' => "ORDER BY pkgnum ASC LIMIT $maxrecords OFFSET $offset",
329 foreach my $cust_pkg ( @packages ) {
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;
336 #arrayref of supplementals
337 $cust_pkg->set('_supplemental', [
338 qsearch('cust_pkg', { main_pkgnum=>$cust_pkg->pkgnum })
342 if ( $cust_pkg->change_to_pkgnum ) {
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);
352 $cust_pkg->{'_cust_pkg_discount_active'} =
353 [ $cust_pkg->cust_pkg_discount_active ];
358 my $cust_pkg = shift;
360 if ( $cust_pkg->change_pkgnum ) {
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);
366 setfrom($changed_from);