default to a session cookie instead of setting an explicit timeout, weird timezone...
[freeside.git] / httemplate / edit / cust_pkg.cgi
1 <%doc>
2
3     Bulk package Edit Page
4
5 </%doc>
6 <& /elements/header-cust_main.html,
7     view              => 'packages',
8     cust_main         => $cust_main,
9     include_selectize => 1,
10 &>
11 <% include('/elements/error.html') %>
12
13 <script>
14
15   let locationnum = "<% $cust_main->ship_locationnum %>";
16
17   function location_changed(e) {
18     locationnum = $(e).val();
19     $('tr[data-locationnum]').find('input').prop('checked', false);
20     $('tr[data-locationnum]').each( function() {
21       let tr_el = $(this);
22       tr_el.css(
23         'display',
24         locationnum == tr_el.data('locationnum') ? 'table-row' : 'none'
25       );
26     });
27   }
28
29   function pkg_class_filter_onchange( selected ) {
30     if ( selected.length == 0 ) {
31       $('tr[data-classnum]').css('display', 'table-row');
32     } else {
33       $('tr[data-classnum]').each( function() {
34         let tr_el = $(this);
35         let classnum = tr_el.data('classnum');
36         let is_displayed = $.grep( selected, function( item ) {
37           return item == classnum;
38         });
39         let display = is_displayed.length ? 'table-row' : 'none';
40         tr_el.css( 'display', is_displayed.length ? 'table-row' : 'none' );
41       });
42     }
43   }
44
45   function confirm_form() {
46     let cust_pkg_removed = [];
47     let pkg_part_added   = [];
48
49     $('input[data-locationnum]:checked').each( function() {
50       let this_el = $(this);
51       cust_pkg_removed.push(
52         '#' + this_el.data('pkgnum') + ' ' + this_el.data('pkg')
53       );
54     });
55
56     $('input[data-pkgpart]').each( function() {
57       qty_el = $(this);
58       qty = qty_el.val();
59
60       if ( qty < 1 ) { return; }
61
62       pkg_part_added.push( qty + ' x ' + qty_el.data('pkg') );
63     });
64
65     if ( cust_pkg_removed.length == 0 ) {
66       cust_pkg_removed.push('No Existing Packages Selected');
67     }
68     if ( pkg_part_added.length == 0 ) {
69       pkg_part_added.push('No New Packages Selected');
70     }
71
72     console.log( cust_pkg_removed );
73     console.log( pkg_part_added );
74
75     confirm_html =
76       '<div style="margin: 1em;">'
77       + '<b><u>Removed Packages:</u></b><br>'
78       + cust_pkg_removed.join('<br>')
79       + '<br><br>'
80       + '<b><u>Replacement Packages:</u></b><br>'
81       + pkg_part_added.join('<br>')
82       + '<br><br>'
83       + '<input type="button" role="button" onclick="submit_form();" value="Confirm Order">'
84       + '</div>';
85
86       overlib(
87         confirm_html,
88         CAPTION, 'Confirm bulk change',
89         STICKY,
90         AUTOSTATUSCAP,
91         MIDX, 0,
92         MIDY, 0,
93         WIDTH, 300,
94         HEIGHT, 200,
95         TEXTSIZE, 3,
96         BGCOLOR, '#ff0000',
97         CGCOLOR, '#ff0000'
98       );
99   }
100
101   function submit_form() {
102     $('#formBulkEdit').submit();
103   }
104 </script>
105
106 <form action="<% $fsurl %>edit/process/cust_pkg.cgi" method="POST" id="formBulkEdit">
107 <input type="hidden" name="custnum" value="<% $custnum %>">
108 <input type="hidden" name="action" value="bulk">
109
110 <p style="margin-bottom: 2em;">
111   <label for="locationnum">Service Location</label>
112   <% include( '/elements/select-cust_location.html',
113       cust_main => $cust_main,
114       addnew    => 0,
115       onchange  => 'javascript:location_changed(this);',
116   ) %><br>
117   <span style="font-size: .8em; padding-left: 1em;">
118     Bulk-edit works with one customer location at a time
119   </span>
120 </p>
121
122 <table style="margin-bottom: 2em;">
123   <thead>
124     <tr style="background-color: #ccc;">
125       <th colspan="2" style="text-align: left;">
126         Pkg #
127       </th>
128       <th style="text-align: left;">
129         Current Packages<br>
130         <div style="font-size: .8em; padding-left: 1em; font-weight: normal;">
131           Selected packages are removed.<br>
132           Attached services are moved to the new package selected below
133         </span>
134       </th>
135     </tr>
136   </thead>
137   <tbody>
138 %   for my $cust_pkg ( @cust_pkg ) {
139 %     my $id = sprintf 'remove_cust_pkg[%s]', $cust_pkg->pkgnum;
140 %     my $is_displayed = $cust_main->ship_locationnum == $cust_pkg->locationnum ? 1 : 0;
141       <tr data-locationnum="<% $cust_pkg->locationnum %>" data-pkg="<% $cust_pkg->pkg |h %>" style="display: <% $is_displayed ? 'table-row' : 'none' %>;">
142         <td>
143           <input type="checkbox"
144                  name="<% $id %>"
145                  id="<% $id %>"
146                  data-pkgnum="<% $cust_pkg->pkgnum %>"
147                  data-locationnum="<% $cust_pkg->locationnum %>"
148                  data-pkg="<% $part_pkg{ $cust_pkg->pkgpart }->pkg |h %>">
149         </td>
150         <td>#<% $cust_pkg->pkgnum %></td>
151         <td>
152           <label for="<% $id %>">
153             <% $part_pkg{ $cust_pkg->pkgpart }->pkg %><br>
154 %           for my $cust_pkg_supp ( @{ $cust_pkg_supp_of{ $cust_pkg->pkgnum }} ) {
155               <span style="font-size: .8em; padding-left: 1em;">
156                 <b>Supplementary:</b> <% $part_pkg{ $cust_pkg_supp->pkgpart }->pkg %>
157               </span>
158             </label>
159 %         }
160         </td>
161       </tr>
162 %   }
163   </tbody>
164 </table>
165
166 <table style="margin-bottom: 2em;">
167   <thead>
168     <tr style="background-color: #ccc;">
169       <th colspan="3">
170         <% include('/elements/selectize/select-multiple-pkg_class.html',
171             id       => 'filter_pkg_class',
172             onchange => 'pkg_class_filter_onchange',
173         ) %>
174       </th>
175     </tr>
176     <tr style="background-color: #ccc;">
177       <th>Qty</th>
178       <th>Class</th>
179       <th style="text-align: left;">Order New Packages</th>
180     </tr>
181   </thead>
182   <tbody>
183 %   for my $part_pkg ( @part_pkg_enabled ) {
184 %     my $id = sprintf 'qty_part_pkg[%s]', $part_pkg->pkgpart;
185       <tr data-classnum="<% $part_pkg->classnum %>">
186         <td>
187           <input type="text"
188                  name="<% $id %>"
189                  id="<% $id %>"
190                  value="0"
191                  size="2"
192                  data-pkgpart="<% $part_pkg->pkgpart %>"
193                  data-pkg="<% $part_pkg->pkg %>">
194           </td>
195         <td><% $part_pkg->classname || '(none)' %></td>
196         <td><% $part_pkg->pkg %></td>
197       </tr>
198 %   }
199   </tbody>
200 </table>
201
202 <input type="button" role="button" value="Order" onclick="confirm_form();">
203
204 </form>
205
206 <% include('/elements/footer.html') %>
207
208 <%init>
209
210 die "access denied"
211   unless $FS::CurrentUser::CurrentUser->access_right('Bulk change customer packages');
212
213 my $custnum = $cgi->param('keywords') || $cgi->param('custnum');
214 $custnum =~ /^\d+$/
215   or die "Invalid custnum($custnum)";
216
217 my $cust_main = qsearchs( cust_main => { custnum => $custnum })
218   or die "Invalid custnum ($custnum)";
219
220 my %part_pkg;
221 my @part_pkg_enabled;
222
223 for my $part_pkg ( qsearch( part_pkg => {} )) {
224   $part_pkg{ $part_pkg->pkgpart } = $part_pkg;
225   push @part_pkg_enabled, $part_pkg
226     unless $part_pkg->disabled;
227 }
228 @part_pkg_enabled =
229   sort { $a->classname cmp $b->classname || $a->pkg cmp $b->pkg }
230   @part_pkg_enabled;
231
232 my @cust_pkg;
233 my %cust_pkg_supp_of;
234 for my $cust_pkg (
235   qsearch(
236     cust_pkg => {
237       custnum  => $custnum,
238       cancel   => '',
239     }
240   )
241 ) {
242   if ( my $main_pkgnum = $cust_pkg->main_pkgnum ) {
243     $cust_pkg_supp_of{ $main_pkgnum } //= [];
244     push @{ $cust_pkg_supp_of{ $main_pkgnum } }, $cust_pkg;
245   } else {
246     $cust_pkg_supp_of{ $cust_pkg->pkgnum } //= [];
247     push @cust_pkg, $cust_pkg;
248   }
249 }
250 </%init>