diff options
author | Mitch Jackson <mitch@freeside.biz> | 2018-11-27 15:39:01 -0500 |
---|---|---|
committer | Mitch Jackson <mitch@freeside.biz> | 2018-11-27 15:44:05 -0500 |
commit | 28051580bf12b079a4631647a5c0defc7f55c9dc (patch) | |
tree | 913528ed9861660fe387ff0bf72db10ee7663d73 /httemplate/edit | |
parent | 0339cd666b4c39ef3772f1d28dc3a5f30f422966 (diff) |
RT# 32243 Package Bulk Edit UI Update
Diffstat (limited to 'httemplate/edit')
-rwxr-xr-x | httemplate/edit/cust_pkg.cgi | 378 | ||||
-rwxr-xr-x | httemplate/edit/process/cust_pkg.cgi | 67 |
2 files changed, 274 insertions, 171 deletions
diff --git a/httemplate/edit/cust_pkg.cgi b/httemplate/edit/cust_pkg.cgi index 7ffbb1fc0..4e4f4d2b2 100755 --- a/httemplate/edit/cust_pkg.cgi +++ b/httemplate/edit/cust_pkg.cgi @@ -1,115 +1,207 @@ -<% include('/elements/header.html', "Add/Edit Packages", '') %> +<%doc> + Bulk package Edit Page + +</%doc> +<& /elements/header-cust_main.html, + view => 'packages', + cust_main => $cust_main, + include_selectize => 1, +&> <% include('/elements/error.html') %> -<FORM ACTION="<% $p1 %>process/cust_pkg.cgi" METHOD=POST> -<INPUT TYPE="hidden" NAME="action" VALUE="bulk"> -<INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>"> - -%#current packages -%if (@cust_pkg) { - - Current packages - select to remove (services are moved to a new package below) - <TABLE> - <TR STYLE="background-color: #cccccc;"> - <TH COLSPAN="2">Pkg #</TH> - <TH>Package description</TH> - </TR> - <BR><BR> -% -% foreach ( @main_pkgs ) { -% my($pkgnum,$pkgpart)=( $_->getfield('pkgnum'), $_->getfield('pkgpart') ); -% my $checked = $remove_pkg{$pkgnum} ? ' CHECKED' : ''; -% -% - - - <TR> - <TD><INPUT TYPE="checkbox" NAME="remove_pkg" VALUE="<% $pkgnum %>"<% $checked %>></TD> - <TD ALIGN="right"><% $pkgnum %>:</TD> - <TD><% $all_pkg{$pkgpart} |h %> - <% $all_comment{$pkgpart} |h %></TD> - </TR> -% foreach my $supp_pkg ( @{ $supp_pkgs_of{$pkgnum} } ) { - <TR> - <TD></TD> - <TD></TD> - <TD>+ <% $all_pkg{$supp_pkg->pkgpart} |h %> - <% $all_comment{$supp_pkg->pkgpart} |h %></TD> - </TR> +<script> + + let locationnum = "<% $cust_main->ship_locationnum %>"; + + function location_changed(e) { + locationnum = $(e).val(); + $('tr[data-locationnum]').find('input').prop('checked', false); + $('tr[data-locationnum]').each( function() { + let tr_el = $(this); + tr_el.css( + 'display', + locationnum == tr_el.data('locationnum') ? 'table-row' : 'none' + ); + }); + } + + function pkg_class_filter_onchange( selected ) { + if ( selected.length == 0 ) { + $('tr[data-classnum]').css('display', 'table-row'); + } else { + $('tr[data-classnum]').each( function() { + let tr_el = $(this); + let classnum = tr_el.data('classnum'); + let is_displayed = $.grep( selected, function( item ) { + return item == classnum; + }); + let display = is_displayed.length ? 'table-row' : 'none'; + tr_el.css( 'display', is_displayed.length ? 'table-row' : 'none' ); + }); + } + } + + function confirm_form() { + let cust_pkg_removed = []; + let pkg_part_added = []; + + $('input[data-locationnum]:checked').each( function() { + let this_el = $(this); + cust_pkg_removed.push( + '#' + this_el.data('pkgnum') + ' ' + this_el.data('pkg') + ); + }); + + $('input[data-pkgpart]').each( function() { + qty_el = $(this); + qty = qty_el.val(); + + if ( qty < 1 ) { return; } + + pkg_part_added.push( qty + ' x ' + qty_el.data('pkg') ); + }); + + if ( cust_pkg_removed.length == 0 ) { + cust_pkg_removed.push('No Existing Packages Selected'); + } + if ( pkg_part_added.length == 0 ) { + pkg_part_added.push('No New Packages Selected'); + } + + console.log( cust_pkg_removed ); + console.log( pkg_part_added ); + + confirm_html = + '<div style="margin: 1em;">' + + '<b><u>Removed Packages:</u></b><br>' + + cust_pkg_removed.join('<br>') + + '<br><br>' + + '<b><u>Replacement Packages:</u></b><br>' + + pkg_part_added.join('<br>') + + '<br><br>' + + '<input type="button" role="button" onclick="submit_form();" value="Confirm Order">' + + '</div>'; + + overlib( + confirm_html, + CAPTION, 'Confirm bulk change', + STICKY, + AUTOSTATUSCAP, + MIDX, 0, + MIDY, 0, + WIDTH, 300, + HEIGHT, 200, + TEXTSIZE, 3, + BGCOLOR, '#ff0000', + CGCOLOR, '#ff0000' + ); + } + + function submit_form() { + $('#formBulkEdit').submit(); + } +</script> + +<form action="<% $fsurl %>edit/process/cust_pkg.cgi" method="POST" id="formBulkEdit"> +<input type="hidden" name="custnum" value="<% $custnum %>"> +<input type="hidden" name="action" value="bulk"> + +<p style="margin-bottom: 2em;"> + <label for="locationnum">Service Location</label> + <% include( '/elements/select-cust_location.html', + cust_main => $cust_main, + addnew => 0, + onchange => 'javascript:location_changed(this);', + ) %><br> + <span style="font-size: .8em; padding-left: 1em;"> + Bulk-edit works with one customer location at a time + </span> +</p> + +<table style="margin-bottom: 2em;"> + <thead> + <tr style="background-color: #ccc;"> + <th colspan="2" style="text-align: left;"> + Pkg # + </th> + <th style="text-align: left;"> + Current Packages<br> + <div style="font-size: .8em; padding-left: 1em; font-weight: normal;"> + Selected packages are removed.<br> + Attached services are moved to the new package selected below + </span> + </th> + </tr> + </thead> + <tbody> +% for my $cust_pkg ( @cust_pkg ) { +% my $id = sprintf 'remove_cust_pkg[%s]', $cust_pkg->pkgnum; +% my $is_displayed = $cust_main->ship_locationnum == $cust_pkg->locationnum ? 1 : 0; + <tr data-locationnum="<% $cust_pkg->locationnum %>" data-pkg="<% $cust_pkg->pkg |h %>" style="display: <% $is_displayed ? 'table-row' : 'none' %>;"> + <td> + <input type="checkbox" + name="<% $id %>" + id="<% $id %>" + data-pkgnum="<% $cust_pkg->pkgnum %>" + data-locationnum="<% $cust_pkg->locationnum %>" + data-pkg="<% $part_pkg{ $cust_pkg->pkgpart }->pkg |h %>"> + </td> + <td>#<% $cust_pkg->pkgnum %></td> + <td> + <label for="<% $id %>"> + <% $part_pkg{ $cust_pkg->pkgpart }->pkg %><br> +% for my $cust_pkg_supp ( @{ $cust_pkg_supp_of{ $cust_pkg->pkgnum }} ) { + <span style="font-size: .8em; padding-left: 1em;"> + <b>Supplementary:</b> <% $part_pkg{ $cust_pkg_supp->pkgpart }->pkg %> + </span> + </label> +% } + </td> + </tr> % } -% } - - - </TABLE> - <BR><BR> -% } - - -Order new packages -<BR><BR> - -%my $cust_main = qsearchs('cust_main',{'custnum'=>$custnum}); -%my $agent = qsearchs('agent',{'agentnum'=> $cust_main->agentnum }); -% -%my %agent_pkgs = map { ( $_->pkgpart => $all_pkg{$_->pkgpart} ) } -% ( qsearch('type_pkgs',{ typenum => $agent->typenum }), -% qsearch('part_pkg', { agentnum => $cust_main->agentnum }), -% ); -% -%my $count = 0; -%my $pkgparts = 0; - -<TABLE> - <TR STYLE="background-color: #cccccc;"> - <TH>Qty.</TH> - <TH COLSPAN="2">Package Description</TH> - </TR> -% -%#foreach my $type_pkgs ( qsearch('type_pkgs',{'typenum'=> $agent->typenum }) ) { -%foreach my $pkgpart ( sort { $agent_pkgs{$a} cmp $agent_pkgs{$b} } -% keys(%agent_pkgs) ) { -% $pkgparts++; -% next unless exists $pkg{$pkgpart}; #skip disabled ones -% #print qq!<TR>! if ( $count == 0 ); -% my $value = $cgi->param("pkg$pkgpart") || 0; -% - - - <TR> - <TD> - <INPUT TYPE="text" NAME="<% "pkg$pkgpart" %>" VALUE="<% $value %>" SIZE="2" MAXLENGTH="2"> - </TD> - <TD ALIGN="right"><% $pkgpart %>:</TD> - <TD><% $pkg{$pkgpart} |h %> - <% $comment{$pkgpart} |h %></TD> - </TR> -% -% $count ++ ; -% #if ( $count == 2 ) { -% # print qq!</TR>\n! ; -% # $count = 0; -% #} -%} -% - - -</TABLE> -% unless ( $pkgparts ) { -% my $p2 = popurl(2); -% my $typenum = $agent->typenum; -% my $agent_type = qsearchs( 'agent_type', { 'typenum' => $typenum } ); -% my $atype = $agent_type->atype; -% - - - (No <A HREF="<% $p2 %>browse/part_pkg.cgi">package definitions</A>, - or agent type - <A HREF="<% $p2 %>edit/agent_type.cgi?<% $typenum %>"><% $atype %></a> - is not allowed to purchase any packages.) -% } - - -<P><INPUT TYPE="submit" VALUE="Order"> - -</FORM> + </tbody> +</table> + +<table style="margin-bottom: 2em;"> + <thead> + <tr style="background-color: #ccc;"> + <th colspan="3"> + <% include('/elements/selectize/select-multiple-pkg_class.html', + id => 'filter_pkg_class', + onchange => 'pkg_class_filter_onchange', + ) %> + </th> + </tr> + <tr style="background-color: #ccc;"> + <th>Qty</th> + <th>Class</th> + <th style="text-align: left;">Order New Packages</th> + </tr> + </thead> + <tbody> +% for my $part_pkg ( @part_pkg_enabled ) { +% my $id = sprintf 'qty_part_pkg[%s]', $part_pkg->pkgpart; + <tr data-classnum="<% $part_pkg->classnum %>"> + <td> + <input type="text" + name="<% $id %>" + id="<% $id %>" + value="0" + size="2" + data-pkgpart="<% $part_pkg->pkgpart %>" + data-pkg="<% $part_pkg->pkg %>"> + </td> + <td><% $part_pkg->classname || '(none)' %></td> + <td><% $part_pkg->pkg %></td> + </tr> +% } + </tbody> +</table> + +<input type="button" role="button" value="Order" onclick="confirm_form();"> + +</form> <% include('/elements/footer.html') %> @@ -118,53 +210,41 @@ Order new packages die "access denied" unless $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages'); -my %pkg = (); -my %comment = (); -my %all_pkg = (); -my %all_comment = (); -#foreach (qsearch('part_pkg', { 'disabled' => '' })) { -# $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); -# $comment{ $_ -> getfield('pkgpart') } = $_->getfield('comment'); -#} -foreach (qsearch('part_pkg', {} )) { - $all_pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); - $all_comment{ $_ -> getfield('pkgpart') } = $_->custom_comment; - next if $_->disabled; - $pkg{ $_ -> getfield('pkgpart') } = $_->getfield('pkg'); - $comment{ $_ -> getfield('pkgpart') } = $_->custom_comment; -} +my $custnum = $cgi->param('keywords') || $cgi->param('custnum'); +$custnum =~ /^\d+$/ + or die "Invalid custnum($custnum)"; -my($custnum, %remove_pkg); -if ( $cgi->param('error') ) { - $custnum = $cgi->param('custnum'); - %remove_pkg = map { $_ => 1 } $cgi->param('remove_pkg'); -} else { - my($query) = $cgi->keywords; - $query =~ /^(\d+)$/; - $custnum = $1; - %remove_pkg = (); -} +my $cust_main = qsearchs( cust_main => { custnum => $custnum }) + or die "Invalid custnum ($custnum)"; -my $p1 = popurl(1); +my %part_pkg; +my @part_pkg_enabled; -my @cust_pkg = qsearch('cust_pkg', { 'custnum' => $custnum, 'cancel' => '' } ); -my @main_pkgs; -my %supp_pkgs_of; # main pkgnum => arrayref of cust_pkgs - - -foreach my $cust_pkg - ( sort { $all_pkg{ $a->pkgpart } cmp $all_pkg{ $b->getfield('pkgpart') } } - @cust_pkg +for my $part_pkg ( qsearch( part_pkg => {} )) { + $part_pkg{ $part_pkg->pkgpart } = $part_pkg; + push @part_pkg_enabled, $part_pkg + unless $part_pkg->disabled; +} +@part_pkg_enabled = + sort { $a->classname cmp $b->classname || $a->pkg cmp $b->pkg } + @part_pkg_enabled; + +my @cust_pkg; +my %cust_pkg_supp_of; +for my $cust_pkg ( + qsearch( + cust_pkg => { + custnum => $custnum, + cancel => '', + } ) - # XXX does not properly handle recursive supplemental links -{ +) { if ( my $main_pkgnum = $cust_pkg->main_pkgnum ) { - $supp_pkgs_of{$main_pkgnum} ||= []; - push @{ $supp_pkgs_of{$main_pkgnum} }, $cust_pkg; + $cust_pkg_supp_of{ $main_pkgnum } //= []; + push @{ $cust_pkg_supp_of{ $main_pkgnum } }, $cust_pkg; } else { - push @main_pkgs, $cust_pkg; - $supp_pkgs_of{$cust_pkg->pkgnum} ||= []; + $cust_pkg_supp_of{ $cust_pkg->pkgnum } //= []; + push @cust_pkg, $cust_pkg; } } - </%init> diff --git a/httemplate/edit/process/cust_pkg.cgi b/httemplate/edit/process/cust_pkg.cgi index c564c417e..82a9e2375 100755 --- a/httemplate/edit/process/cust_pkg.cgi +++ b/httemplate/edit/process/cust_pkg.cgi @@ -5,38 +5,61 @@ <% $cgi->redirect(popurl(3). "view/cust_main.cgi?$custnum") %> % } <%init> - +use Data::Dumper; +my $DEBUG = 0; my $curuser = $FS::CurrentUser::CurrentUser; die "access denied" unless $curuser->access_right('Bulk change customer packages'); my $error = ''; +my %param = $cgi->Vars; + +my $custnum = $param{custnum}; +$error = "Invalid custnum ($custnum)" if $custnum =~ /\D/; + +my $locationnum = $param{locationnum}; +$error = "Invalid locationnum ($locationnum)" if $locationnum =~ /\D/; + +my @remove_pkgnum = + map { $_ =~ /remove_cust_pkg\[(\d+)\]/ ? $1 : () } + keys %param; + +my @pkgparts; +for my $k ( keys %param ) { + next unless $k =~ /qty_part_pkg\[(\d+)\]/; + my $pkgpart = $1; + my $qty = $param{$k}; + $qty =~ s/(^\s+|\s+$)//g; -#untaint custnum -$cgi->param('custnum') =~ /^(\d+)$/; -my $custnum = $1; - -my @remove_pkgnums = map { - /^(\d+)$/ or die "Illegal remove_pkg value!"; - $1; -} $cgi->param('remove_pkg'); - -my( $action, $error_redirect ) = ( '', '' ); -my @pkgparts = (); - -foreach my $pkgpart ( map /^pkg(\d+)$/ ? $1 : (), $cgi->param ) { - if ( $cgi->param("pkg$pkgpart") =~ /^(\d+)$/ ) { - my $num_pkgs = $1; - while ( $num_pkgs-- ) { - push @pkgparts,$pkgpart; - } - } else { - $error = "Illegal quantity"; + warn "k($k) param{k}($param{$k}) pkgpart($pkgpart) qty($qty)\n" + if $DEBUG; + + if ( $qty =~ /\D/ ) { + $error = "Invalid quantity $qty for pkgpart $pkgpart - please use a number"; last; } + + next if $qty == 0; + + push ( @pkgparts, $pkgpart ) for ( 1..$qty ); +} + +if ( $DEBUG ) { + warn Dumper({ + custnum => $custnum, + locationnum => $locationnum, + remove_pkgnum => \@remove_pkgnum, + pkgparts => \@pkgparts, + param => \%param, + }); } -$error ||= FS::cust_pkg::order($custnum,\@pkgparts,\@remove_pkgnums); +$error ||= FS::cust_pkg::order({ + custnum => $custnum, + pkgparts => \@pkgparts, + remove_pkgnum => \@remove_pkgnum, + locationnum => $locationnum, +}); </%init> |