diff options
Diffstat (limited to 'httemplate')
-rw-r--r-- | httemplate/browse/part_fee.html | 71 | ||||
-rw-r--r-- | httemplate/edit/credit-cust_bill_pkg.html | 3 | ||||
-rw-r--r-- | httemplate/edit/part_fee.html | 141 | ||||
-rwxr-xr-x | httemplate/edit/process/part_fee.html | 20 | ||||
-rw-r--r-- | httemplate/elements/menu.html | 4 | ||||
-rw-r--r-- | httemplate/misc/xmlhttp-calculate_taxes.html | 9 | ||||
-rw-r--r-- | httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html | 15 | ||||
-rw-r--r-- | httemplate/pref/pref-process.html | 1 | ||||
-rw-r--r-- | httemplate/pref/pref.html | 16 | ||||
-rw-r--r-- | httemplate/search/cust_bill_pkg.cgi | 21 | ||||
-rwxr-xr-x | httemplate/view/cust_main/packages.html | 232 | ||||
-rw-r--r-- | httemplate/view/cust_main/packages/hidden.html | 55 | ||||
-rwxr-xr-x | httemplate/view/cust_main/packages/section.html | 19 |
13 files changed, 496 insertions, 111 deletions
diff --git a/httemplate/browse/part_fee.html b/httemplate/browse/part_fee.html new file mode 100644 index 000000000..482c692d7 --- /dev/null +++ b/httemplate/browse/part_fee.html @@ -0,0 +1,71 @@ +<& elements/browse.html, + title => 'Fee definitions', + name_singular => 'fee definition', + query => $query, + count_query => $count_query, + header => [ '#', + 'Description', + 'Comment', + 'Class', + 'Amount', + 'Tax status', + ], + fields => [ 'feepart', + 'itemdesc', + 'comment', + 'classname', + $sub_amount, + $sub_tax, + ], + disableable => 1, + disabled_statuspos => 3, + agent_pos => 6, + agent_virt => 1, + agent_null_right=> 'Edit global fee definitions', + links => [ '', + $link, + $link, + ], + align => 'cllccc', + menubar => \@menubar, +&> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; +my $acl_edit = $curuser->access_right('Edit fee definitions'); +my $acl_edit_global = $curuser->access_right('Edit global fee definitions'); +die "access denied" + unless $acl_edit or $acl_edit_global; + +my $query = { + 'select' => 'part_fee.*,'. + '(select classname from pkg_class '. + 'where pkg_class.classnum = part_fee.classnum) AS classname', + 'table' => 'part_fee', +}; +my $count_query = "SELECT COUNT(*) FROM part_fee"; + +my $sub_amount = sub { + my $obj = shift; + my $string = $obj->explanation; + $string =~ s/\n/<br>/sg; + $string; +}; + +my $sub_tax = sub { + my $obj = shift; + if ( $obj->taxable ) { + return $obj->taxclass || 'taxable'; + } elsif ( $obj->taxproductnum ) { + return join('<br>', + split(/\s*:\s*/, $obj->part_pkg_taxproduct->description) + ); + } else { + return 'exempt'; + } +}; + +my $link = [ $p.'edit/part_fee.html?', 'feepart' ]; + +my @menubar = ( 'Add a new fee definition', + $p.'edit/part_fee.html' ); +</%init> diff --git a/httemplate/edit/credit-cust_bill_pkg.html b/httemplate/edit/credit-cust_bill_pkg.html index a5ecb69e3..40faddc46 100644 --- a/httemplate/edit/credit-cust_bill_pkg.html +++ b/httemplate/edit/credit-cust_bill_pkg.html @@ -269,7 +269,8 @@ my @cust_bill_pkg = qsearch({ 'select' => 'cust_bill_pkg.*', 'table' => 'cust_bill_pkg', 'addl_from' => 'LEFT JOIN cust_bill USING (invnum)', - 'extra_sql' => "WHERE custnum = $custnum AND pkgnum != 0", + 'extra_sql' => "WHERE custnum = $custnum ". + "AND (pkgnum != 0 or feepart IS NOT NULL)", 'order_by' => 'ORDER BY invnum ASC, billpkgnum ASC', }); diff --git a/httemplate/edit/part_fee.html b/httemplate/edit/part_fee.html new file mode 100644 index 000000000..dada23360 --- /dev/null +++ b/httemplate/edit/part_fee.html @@ -0,0 +1,141 @@ +<& elements/edit.html, + 'name_singular' => 'fee definition', + 'table' => 'part_fee', + 'labels' => { + 'feepart' => 'Fee definition', + 'itemdesc' => 'Description', + 'comment' => 'Comment (customer-hidden)', + 'classnum' => 'Package class', + 'taxable' => 'This fee is taxable', + 'disabled' => 'Disable this fee', + 'taxclass' => 'Tax class name', + 'taxproductnum' => 'Tax product', + 'pay_weight' => 'Payment weight', + 'credit_weight' => 'Credit weight', + 'agentnum' => 'Agent', + 'amount' => 'Flat fee amount', + 'percent' => 'Percentage of invoice amount', + 'basis' => 'Based on', + 'setuprecur' => 'Report this fee as', + 'minimum' => 'Minimum fee', + 'maximum' => 'Maximum fee', + 'limit_credit' => 'Limit to customer credit balance', + %locale_labels + }, + 'fields' => \@fields, + 'edit_callback' => $edit_callback, + 'error_callback' => $error_callback, +&> +<%init> +my $curuser = $FS::CurrentUser::CurrentUser; +my $acl_edit = $curuser->access_right('Edit fee definitions'); +my $acl_edit_global = $curuser->access_right('Edit global fee definitions'); +die "access denied" + unless $acl_edit or $acl_edit_global; + +my $conf = FS::Conf->new; +my @tax_fields; +if ( $conf->exists('enable_taxproducts') ) { + @tax_fields = ( + { field => 'taxproductnum', type => 'select-taxproduct' } + ); +} else { + @tax_fields = ( + { field => 'taxable', type => 'checkbox', value => 'Y' }, + ); + push ( + { field => 'taxclass', type => 'select-taxclass' }, + ) if $conf->exists('enable_taxclasses'); +} + +my $default_locale = $conf->config('locale') || 'en_US'; +my @locales = grep {$_ ne $default_locale} $conf->config('available-locales'); +# duplicates edit/part_pkg.cgi, yuck +my $n = 0; +my (@locale_fields, %locale_labels); +foreach (@locales) { + push @locale_fields, + { field => 'feepartmsgnum'. $n, type => 'hidden' }, + { field => 'feepartmsgnum'. $n. '_locale', type => 'hidden' }, + { field => 'feepartmsgnum'. $n. '_itemdesc', type => 'text', size => 40 }, + ; + $locale_labels{ 'feepartmsgnum'.$n.'_itemdesc' } = + 'Description—' . FS::Locales->description($_); + $n++; +} + +my @fields = ( + + { field => 'itemdesc', type => 'text', size => 40, }, + @locale_fields, + + { field => 'comment', type => 'text', size => 40, }, + + { field => 'agentnum', + type => 'select-agent', + disable_empty => !$acl_edit_global, + empty_label => '(global)', + }, + + { field => 'classnum', + type => 'select-pkg_class', + }, + + { field => 'disabled', + type => 'checkbox', + value => 'Y', + }, + + { field => 'setuprecur', + type => 'select', + options => [ 'setup', 'recur' ], + labels => { 'setup' => 'a setup fee', + 'recur' => 'a recurring charge' }, + }, + + { type => 'justtitle', value => 'Fee calculation' }, + { field => 'amount', type => 'money', }, + { field => 'percent', type => 'percentage', }, + + { field => 'basis', + type => 'select', + options => [ 'charged', 'owed' ], + labels => { 'charged' => 'amount charged', + 'owed' => 'balance due', }, + }, + + { field => 'minimum', type => 'money', }, + { field => 'maximum', type => 'money', }, + { field => 'limit_credit', + type => 'checkbox', + value => 'Y' }, + + { type => 'justtitle', value => 'Taxation' }, + + @tax_fields, +); + +my $edit_callback = sub { + my ($cgi, $obj, $fields, $opt) = @_; + my %existing_locales; + if ( $obj->feepart ) { + %existing_locales = map { $_->locale => $_ } $obj->part_fee_msgcat; + } + my $n = 0; + foreach (@locales) { + $obj->set('feepartmsgnum'.$n.'_locale', $_); + # load the existing itemdescs + if ( my $msgcat = $existing_locales{$_} ) { + $obj->set('feepartmsgnum'.$n, $msgcat->feepartmsgnum); + $obj->set('feepartmsgnum'.$n.'_itemdesc', $msgcat->itemdesc); + } + # then override that with the CGI param if there is one + if ( my $itemdesc = $cgi->param('feepartmsgnum'.$n.'_itemdesc') ) { + $obj->set('feepartmsgnum'.$n.'_itemdesc', $itemdesc); + } + $n++; + } +}; + +my $error_callback = $edit_callback; +</%init> diff --git a/httemplate/edit/process/part_fee.html b/httemplate/edit/process/part_fee.html new file mode 100755 index 000000000..25656e9b0 --- /dev/null +++ b/httemplate/edit/process/part_fee.html @@ -0,0 +1,20 @@ +<& elements/process.html, + 'debug' => 1, + 'table' => 'part_fee', + 'agent_virt' => 1, + 'agent_null_right' => 'Edit global fee definitions', + 'viewall_dir' => 'browse', + 'process_o2m' => { + 'table' => 'part_fee_msgcat', + 'fields' => [ 'locale', 'itemdesc' ], + }, +&> +<%init> + +my $curuser = $FS::CurrentUser::CurrentUser; +my $acl_edit = $curuser->access_right('Edit fee definitions'); +my $acl_edit_global = $curuser->access_right('Edit global fee definitions'); +die "access denied" + unless $acl_edit or $acl_edit_global; + +</%init> diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html index fb84e7501..cd4fb39ec 100644 --- a/httemplate/elements/menu.html +++ b/httemplate/elements/menu.html @@ -584,6 +584,10 @@ if ( $curuser->access_right('Configuration') ) { $config_pkg{'Package report classes'} = [ $fsurl.'browse/part_pkg_report_option.html', 'Package classes define optional groups of packages for reporting only.' ]; #eo package grouping sub-menu + if ( $curuser->access_right([ 'Edit fee definitions', + 'Edit global fee definitions' ]) ) { + $config_pkg{'Fees'} = [ $fsurl.'browse/part_fee.html', '' ]; + } $config_pkg{'Discounts'} = [ $fsurl.'browse/discount.html', '' ]; $config_pkg{'Discount classes'} = [ $fsurl.'browse/discount_class.html', '' ]; $config_pkg{'Cancel/Suspend Reasons'} = [ \%config_pkg_reason, '' ]; diff --git a/httemplate/misc/xmlhttp-calculate_taxes.html b/httemplate/misc/xmlhttp-calculate_taxes.html index ed7bd0173..2bb1f4cce 100644 --- a/httemplate/misc/xmlhttp-calculate_taxes.html +++ b/httemplate/misc/xmlhttp-calculate_taxes.html @@ -62,14 +62,7 @@ if ( $sub eq 'calculate_taxes' ) { my $taxlisthash = {}; foreach my $cust_bill_pkg (values %cust_bill_pkg) { - my $part_pkg = $cust_bill_pkg->part_pkg; - $cust_main->_handle_taxes( $part_pkg, - $taxlisthash, - $cust_bill_pkg, - $cust_bill_pkg->cust_pkg, - $cust_bill_pkg->cust_bill->_date, - $cust_bill_pkg->cust_pkg->pkgpart, - ); + $cust_main->_handle_taxes( $taxlisthash, $cust_bill_pkg ); } my $listref_or_error = $cust_main->calculate_taxes( [ values %cust_bill_pkg ], $taxlisthash, [ values %cust_bill_pkg ]->[0]->cust_bill->_date ); diff --git a/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html index c0db3e2c4..4558682bd 100644 --- a/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html +++ b/httemplate/misc/xmlhttp-cust_bill_pkg-calculate_taxes.html @@ -62,15 +62,7 @@ if ( $sub eq 'calculate_taxes' ) { push @cust_bill_pkg, $cust_bill_pkg; - my $part_pkg = $cust_bill_pkg->part_pkg; - $cust_main->_handle_taxes( $part_pkg, - $taxlisthash, - $cust_bill_pkg, - $cust_bill_pkg->cust_pkg, - $cust_bill_pkg->cust_bill->_date, - $cust_bill_pkg->cust_pkg->pkgpart, - ); - + $cust_main->_handle_taxes( $taxlisthash, $cust_bill_pkg ); } if ( @cust_bill_pkg ) { @@ -89,7 +81,10 @@ if ( $sub eq 'calculate_taxes' ) { foreach my $taxline ( @$listref_or_error ) { my $amount = $taxline->setup; my $desc = $taxline->desc; - foreach my $location ( @{$taxline->cust_bill_pkg_tax_location}, @{$taxline->cust_bill_pkg_tax_rate_location} ) { + foreach my $location ( + @{$taxline->get('cust_bill_pkg_tax_location')}, + @{$taxline->get('cust_bill_pkg_tax_rate_location')} ) + { my $taxlocnum = $location->locationnum || ''; my $taxratelocnum = $location->taxratelocationnum || ''; $location->cust_bill_pkg_desc($taxline->desc); #ugh @ that kludge diff --git a/httemplate/pref/pref-process.html b/httemplate/pref/pref-process.html index 7848b72cb..6d4f89a77 100644 --- a/httemplate/pref/pref-process.html +++ b/httemplate/pref/pref-process.html @@ -59,6 +59,7 @@ unless ( $error ) { # if ($access_user) { snom-ip snom-username snom-password vonage-fromnumber vonage-username vonage-password cust_pkg-display_times + hide_package_changes show_pkgnum show_confitem_counts export_getsettings show_db_profile save_db_profile save_tmp_typesetting height width availHeight availWidth colorDepth diff --git a/httemplate/pref/pref.html b/httemplate/pref/pref.html index ccfeecd77..eaa7d3281 100644 --- a/httemplate/pref/pref.html +++ b/httemplate/pref/pref.html @@ -78,6 +78,22 @@ Interface </TD> </TR> + <TR> + <TH ALIGN="right">Hide package changes: </TH> + <TD> + <& /elements/select.html, + field => 'hide_package_changes', + options => [ '', 'location', 'all' ], + labels => { '' => 'never', + 'location' => 'location changes', + 'all' => 'all package changes', + }, + curr_value => ($cgi->param('hide_package_changes') + || $curuser->option('hide_package_changes')), + &> + </TD> + </TR> + % my $history_order = $curuser->option('history_order') || 'oldest'; <TR> <TH ALIGN="right">Customer history sort order: </TH> diff --git a/httemplate/search/cust_bill_pkg.cgi b/httemplate/search/cust_bill_pkg.cgi index 6b7a5e6e2..440ab150c 100644 --- a/httemplate/search/cust_bill_pkg.cgi +++ b/httemplate/search/cust_bill_pkg.cgi @@ -137,9 +137,9 @@ Filtering parameters: - use_override: Apply "classnum" and "taxclass" filtering based on the override (bundle) pkgpart, rather than always using the true pkgpart. -- nottax: Limit to items that are not taxes (pkgnum > 0). +- nottax: Limit to items that are not taxes (pkgnum > 0 or feepart > 0). -- istax: Limit to items that are taxes (pkgnum == 0). +- istax: Limit to items that are taxes (pkgnum == 0 and feepart = null). - taxnum: Limit to items whose tax definition matches this taxnum. With "nottax" that means items that are subject to that tax; @@ -305,7 +305,8 @@ if ( $cgi->param('custnum') =~ /^(\d+)$/ ) { # we want the package and its definition if available my $join_pkg = ' LEFT JOIN cust_pkg USING (pkgnum) - LEFT JOIN part_pkg USING (pkgpart)'; + LEFT JOIN part_pkg USING (pkgpart) + LEFT JOIN part_fee USING (feepart)'; my $part_pkg = 'part_pkg'; # "Separate sub-packages from parents" @@ -319,12 +320,16 @@ if ( $use_override ) { $part_pkg = 'override'; } push @select, "$part_pkg.pkgpart", "$part_pkg.pkg"; -push @select, "$part_pkg.taxclass" if $conf->exists('enable_taxclasses'); +push @select, "COALESCE($part_pkg.taxclass, part_fee.taxclass) AS taxclass" + if $conf->exists('enable_taxclasses'); # the non-tax case if ( $cgi->param('nottax') ) { - push @where, 'cust_bill_pkg.pkgnum > 0'; + push @select, "part_fee.itemdesc"; + + push @where, + '(cust_bill_pkg.pkgnum > 0 OR cust_bill_pkg.feepart IS NOT NULL)'; my @tax_where; # will go into a subquery my @exempt_where; # will also go into a subquery @@ -335,7 +340,7 @@ if ( $cgi->param('nottax') ) { # N: classnum if ( grep { $_ eq 'classnum' } $cgi->param ) { my @classnums = grep /^\d*$/, $cgi->param('classnum'); - push @where, "COALESCE($part_pkg.classnum, 0) IN ( ". + push @where, "COALESCE(part_fee.classnum, $part_pkg.classnum, 0) IN ( ". join(',', @classnums ). ' )' if @classnums; @@ -360,7 +365,7 @@ if ( $cgi->param('nottax') ) { # effective taxclass, not the real one push @tax_where, 'cust_main_county.taxclass IS NULL' } elsif ( $cgi->param('taxclass') ) { - push @tax_where, "$part_pkg.taxclass IN (" . + push @tax_where, "COALESCE(part_fee.taxclass, $part_pkg.taxclass) IN (" . join(', ', map {dbh->quote($_)} $cgi->param('taxclass') ). ')'; } @@ -681,7 +686,7 @@ if ( $cgi->param('salesnum') =~ /^(\d+)$/ ) { 'paid' => ($cgi->param('paid') ? 1 : 0), 'classnum' => scalar($cgi->param('classnum')) ); - $join_pkg .= " JOIN sales_pkg_class ON ( COALESCE(sales_pkg_class.classnum, 0) = COALESCE( part_pkg.classnum, 0) )"; + $join_pkg .= " JOIN sales_pkg_class ON ( COALESCE(sales_pkg_class.classnum, 0) = COALESCE( part_fee.classnum, part_pkg.classnum, 0) )"; my $extra_sql = $subsearch->{extra_sql}; $extra_sql =~ s/^WHERE//; diff --git a/httemplate/view/cust_main/packages.html b/httemplate/view/cust_main/packages.html index 566ab2943..746e0c7c5 100755 --- a/httemplate/view/cust_main/packages.html +++ b/httemplate/view/cust_main/packages.html @@ -22,8 +22,62 @@ table.usage { .row0 { background-color: #eeeeee; } .row1 { background-color: #ffffff; } -</STYLE> +table.hiddenrows { + width: 80%; + margin-left: 100px; + border: 1px solid #7E0079; + background-color: #cccccc; +} + +.hiddenrows td { + text-align: center; +} +.rolldown_button { + min-width: 80px; + margin-left: 100px; + min-height: 20px; + background-color: #efefef; + border: 1px solid #7e0079; + z-index: 1; + text-align: center; +} +</STYLE> +% # activate rolldown buttons for hidden package blocks +<SCRIPT TYPE="text/javascript"> +function toggle_rolldown() { + var up_arrow = <% decode_entities('⬆') |js_string %>; + var dn_arrow = <% decode_entities('⬇') |js_string %>; + var pkgnum = this.id.replace('rolldown_', ''); + var hidden = document.getElementById('cust_pkg'+pkgnum+'_block'); + if (hidden.style.display == 'none') { + hidden.style.display = ''; + this.textContent = this.textContent.replace(dn_arrow, up_arrow); + } else { + hidden.style.display = 'none'; + this.textContent = this.textContent.replace(up_arrow, dn_arrow); + } +} +<&| /elements/onload.js &> +var el; +% if ( $cgi->param('fragment') =~ /^cust_pkg(\d+)$/ ) { +% # IE-specific hack, but also unhide the row if it's in a hidden block +el = document.getElementById('cust_pkg<% $1 %>'); +% } +var all_buttons = document.getElementsByClassName('rolldown_button'); +for (var i = 0; i < all_buttons.length; i++) { + all_buttons[i].onclick = toggle_rolldown; + var block_id = all_buttons[i].id.replace('rolldown_', ''); + if ( el && document.getElementById('cust_pkg'+block_id+'_block') + .contains(el) + ) { + // then toggle it now + all_buttons[i].click(); + } +} +if ( el ) el.scrollIntoView(true); +</&> +</SCRIPT> % unless ( $opt{no_links} ) { % my $s = 0; @@ -124,7 +178,7 @@ table.usage { % if ( $conf->exists('cust_pkg-group_by_location') ) { <& locations.html, 'cust_main' => $cust_main, - 'packages' => $packages, + 'packages' => \@packages, %opt, &> % } @@ -133,7 +187,7 @@ table.usage { <& /elements/table-grid.html &> <& packages/section.html, 'cust_main' => $cust_main, - 'packages' => $packages, + 'packages' => \@packages, %opt, &> </TABLE> @@ -141,15 +195,6 @@ table.usage { </TD> </TR> -% if ( $cgi->param('fragment') =~ /^cust_pkg(\d+)$/ ) { - <SCRIPT> - // IE-specific hack. other browsers listen to #fragments - // is this even working? or is the #target redirection just working cause - // we set the URL params differently? - var el = document.getElementById( 'cust_pkg<% $1 %>' ); - if ( el ) el.scrollIntoView(true); - </SCRIPT> -% } </TABLE> <%init> @@ -159,94 +204,113 @@ my $conf = new FS::Conf; my $curuser = $FS::CurrentUser::CurrentUser; -my( $packages, $num_old_packages ) = get_packages($cust_main, $conf); - my $countrydefault = scalar($conf->config('countrydefault')) || 'US'; -#subroutines - -sub get_packages { - my $cust_main = shift or return undef; - my $conf = shift; - - my $method; - if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me - || ( $conf->exists('hidecancelledpackages') - && ! $cgi->param('showcancelledpackages') ) - ) - { - $method = 'ncancelled_pkgs'; - } else { - $method = 'all_pkgs'; - } - my $cust_pkg_fields = - join(', ', map { "cust_pkg.$_ AS $_" } fields('cust_pkg') ); +my $hide_changed = $curuser->option('hide_package_changes'); - my $part_pkg_fields = - join(', ', map { "part_pkg.$_ AS part_pkg_$_" } fields('part_pkg') ); +my $hide_cancelled = 0; +if ( $cgi->param('showcancelledpackages') eq '0' #see if it was set by me + || ( $conf->exists('hidecancelledpackages') + && ! $cgi->param('showcancelledpackages') ) + ) +{ + $hide_cancelled = 1; +} - my $group_by = - join(', ', map "cust_pkg.$_", fields('cust_pkg') ). ', '. - join(', ', map "part_pkg.$_", fields('part_pkg') ); +my $cust_pkg_fields = + join(', ', map { "cust_pkg.$_ AS $_" } fields('cust_pkg') ); - my $num_svcs = '( SELECT COUNT(*) FROM cust_svc '. - ' WHERE cust_svc.pkgnum = cust_pkg.pkgnum ) AS num_svcs'; +my $part_pkg_fields = + join(', ', map { "part_pkg.$_ AS part_pkg_$_" } fields('part_pkg') ); - my @packages = $cust_main->$method( { - 'select' => "$cust_pkg_fields, $part_pkg_fields, $num_svcs", - 'addl_from' => 'LEFT JOIN part_pkg USING ( pkgpart )', - } ); - my $num_old_packages = scalar(@packages); +my $group_by = + join(', ', map "cust_pkg.$_", fields('cust_pkg') ). ', '. + join(', ', map "part_pkg.$_", fields('part_pkg') ); - my %change_to_from; # target pkgnum => current cust_pkg, for future changes +my $num_svcs = '( SELECT COUNT(*) FROM cust_svc '. + ' WHERE cust_svc.pkgnum = cust_pkg.pkgnum ) AS num_svcs'; - foreach my $cust_pkg ( @packages ) { - my %hash = $cust_pkg->hash; - my %part_pkg = map { /^part_pkg_(.+)$/ or die; ( $1 => $hash{$_} ); } - grep { /^part_pkg_/ } keys %hash; - $cust_pkg->{'_pkgpart'} = new FS::part_pkg \%part_pkg; - if ( $cust_pkg->change_to_pkgnum ) { - $change_to_from{$cust_pkg->change_to_pkgnum} = $cust_pkg; - } +# don't exclude cancelled packages at this stage +my @packages = $cust_main->all_pkgs( { + 'select' => "$cust_pkg_fields, $part_pkg_fields, $num_svcs", + 'addl_from' => 'LEFT JOIN part_pkg USING ( pkgpart )', +} ); + +my %change_to_from; # target pkgnum => current cust_pkg, for future changes +my %changed_from; # old pkgnum => new cust_pkg, for past changes + +foreach my $cust_pkg ( @packages ) { + my %hash = $cust_pkg->hash; + my %part_pkg = map { /^part_pkg_(.+)$/ or die; ( $1 => $hash{$_} ); } + grep { /^part_pkg_/ } keys %hash; + $cust_pkg->{'_pkgpart'} = new FS::part_pkg \%part_pkg; + if ( $cust_pkg->change_to_pkgnum ) { + $change_to_from{$cust_pkg->change_to_pkgnum} = $cust_pkg; } + if ( $cust_pkg->change_pkgnum ) { + $changed_from{$cust_pkg->change_pkgnum} = $cust_pkg; + } +} - if ( keys %change_to_from ) { - my @not_future_packages; - foreach my $cust_pkg (@packages) { - if ( exists( $change_to_from{$cust_pkg->pkgnum} ) ) { - my $change_from = $change_to_from{ $cust_pkg->pkgnum }; - $cust_pkg->set('change_from_pkg', $change_from); - $change_from->set('change_to_pkg', $cust_pkg); - } else { - push @not_future_packages, $cust_pkg; +# filter out hidden package changes +if ( keys %change_to_from or keys %changed_from ) { + my @displayable_packages; + foreach my $cust_pkg (@packages) { + if ( exists( $change_to_from{$cust_pkg->pkgnum} ) ) { + # $cust_pkg is an ordered, not-yet-active package change target + my $change_from = $change_to_from{ $cust_pkg->pkgnum }; + $cust_pkg->set('change_from_pkg', $change_from); + $change_from->set('change_to_pkg', $cust_pkg); + } elsif ( exists( $changed_from{$cust_pkg->pkgnum} ) ) { + # $cust_pkg is a canceled package changed into another packge + my $changed_to = $changed_from{$cust_pkg->pkgnum}; + if ( ( $hide_changed eq 'all' ) or + ( $hide_changed eq 'location' + and $changed_to->pkgpart == $cust_pkg->pkgpart + and $changed_to->refnum == $cust_pkg->refnum + and $changed_to->quantity == $cust_pkg->quantity ) + ) { + # then we're hiding it + $cust_pkg->set('changed_to_pkg', $changed_to); + $changed_to->set('changed_from_pkg', $cust_pkg); + } else { # show it anyway + push @displayable_packages, $cust_pkg; } + } else { + push @displayable_packages, $cust_pkg; } - @packages = @not_future_packages; } + @packages = @displayable_packages; +} - unless ( $cgi->param('showoldpackages') ) { - my $years = $conf->config('cust_main-packages-years') || 2; - my $then = time - $years * 31556926; #60*60*24*365.2422 is close enough - - my %hide = ( 'cancelled' => 'cancel', - 'one-time charge' => 'setup', - ); - - @packages = - grep { !exists($hide{$_->status}) or $_->get($hide{$_->status}) > $then - or $_->num_svcs #don't hide packages w/services - } - @packages; - } +# filter all cancelled packages if the user wants +if ( $hide_cancelled ) { + @packages = grep { !$_->get('cancel') } @packages; +} + +# filter out 'old' packages +my $num_old_packages = scalar(@packages); - $num_old_packages -= scalar(@packages); - - # don't include supplemental packages in this list; they'll be found from - # their main packages - # (as will change-target packages) - @packages = grep !$_->main_pkgnum, @packages; +unless ( $cgi->param('showoldpackages') ) { + my $years = $conf->config('cust_main-packages-years') || 2; + my $then = time - $years * 31556926; #60*60*24*365.2422 is close enough - ( \@packages, $num_old_packages ); + my %hide = ( 'cancelled' => 'cancel', + 'one-time charge' => 'setup', + ); + + @packages = + grep { !exists($hide{$_->status}) or $_->get($hide{$_->status}) > $then + or $_->num_svcs #don't hide packages w/services + } + @packages; } +$num_old_packages -= scalar(@packages); + +# don't include supplemental packages in this list; they'll be found from +# their main packages +# (as will change-target packages) +@packages = grep !$_->main_pkgnum, @packages; + </%init> diff --git a/httemplate/view/cust_main/packages/hidden.html b/httemplate/view/cust_main/packages/hidden.html new file mode 100644 index 000000000..e3bd0fabf --- /dev/null +++ b/httemplate/view/cust_main/packages/hidden.html @@ -0,0 +1,55 @@ +% if (!$iopt{noframe}) { +% # then start the block here, and assign a suitable ID (cust_pkgX_block) +<TABLE CLASS="hiddenrows" STYLE="display: none" ID="<% $id %>_block"> +% } + <TR ID="<% $id %>"> + <TD> + <A NAME="<% $id %>"/> + <% $pkgnum %> + </TD> + <TD> +% if ( $pkgpart_change and $location_change ) { + Package type and location change +% } elsif ( $pkgpart_change ) { + Package type change +% } elsif ( $location_change ) { + Location change +% } # or else what? + <B><% time2str('%b %o, %Y', $cust_pkg->get('cancel')) %></B> + </TD><TD> +% if ( $pkgpart_change ) { + from <B><% $part_pkg->pkg |h %></B></A> - <% $part_pkg->custom_comment |h %> +% } +% if ( $pkgpart_change and $location_change ) { + <BR> +% } +% if ( $location_change ) { + from <I><% $cust_pkg->location_label %></I> +% } + </TD> + </TR> +% if ( $cust_pkg->get('changed_from_pkg') ) { +<& hidden.html, $cust_pkg->get('changed_from_pkg'), + %iopt, + 'next_pkg' => $cust_pkg, + 'noframe' => 1 +&> +% } +% if ( !$iopt{noframe} ) { +</TABLE> +% } +<%init> +my $cust_pkg = shift; +my $part_pkg = $cust_pkg->part_pkg; +my %iopt = @_; +my $next = delete($iopt{'next_pkg'}); +my $curuser = $FS::CurrentUser::CurrentUser; +my $pkgnum = $curuser->option('show_pkgnum') ? $cust_pkg->pkgnum.': ' : ''; + +my $id = "cust_pkg".$cust_pkg->pkgnum; + +my $pkgpart_change = ($next->pkgpart != $cust_pkg->pkgpart); +my $location_change = ($next->locationnum != $cust_pkg->locationnum); +my $both_change = $pkgpart_change && $location_change; + +</%init> diff --git a/httemplate/view/cust_main/packages/section.html b/httemplate/view/cust_main/packages/section.html index 152ccaa5d..730bb2cf0 100755 --- a/httemplate/view/cust_main/packages/section.html +++ b/httemplate/view/cust_main/packages/section.html @@ -35,6 +35,25 @@ </TD> <& services.html, %iopt &> </TR> +% # insert hidden predecessors to this package, if any +% # and a rolldown button to show them +% # (we'll make it do something later) +% if ( $cust_pkg->get('changed_from_pkg') ) { + <TR CLASS="row<% $row % 2 %>"> + <TD COLSPAN=4> + <BUTTON CLASS="rolldown_button" + ID="rolldown_<% $cust_pkg->change_pkgnum %>"> + History ⬇ + </BUTTON> +% # the hidden block here has ID="cust_pkgX" where X is the first pkgnum +% # it contains. + <& hidden.html, $cust_pkg->get('changed_from_pkg'), + %iopt, + 'next_pkg' => $cust_pkg, + &> + </TD> + </TR> +% } % $row++; % # show the change target, if there is one % if ( $cust_pkg->change_to_pkg ) { |