fix "-" character in one-time charge descriptions, #72101, from #72175
[freeside.git] / httemplate / edit / quick-charge.html
1 <& /elements/header.html, mt('One-time charge'), '',
2             ( ($quotationnum || $cgi->param('error')) ? '' : 'onload="addRow()"' ),
3 &>
4
5 <LINK REL="stylesheet" TYPE="text/css" HREF="<%$fsurl%>elements/calendar-win2k-2.css" TITLE="win2k-2">
6 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar_stripped.js"></SCRIPT>
7 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-en.js"></SCRIPT>
8 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/calendar-setup.js"></SCRIPT>
9 <SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/xregexp-all.js"></SCRIPT>
10
11 <& /elements/error.html &>
12
13 <SCRIPT TYPE="text/javascript">
14
15 function enable_quick_charge (e) {
16
17   if (    document.QuickChargeForm.amount.value
18        && document.QuickChargeForm.pkg.value    ) {
19     document.QuickChargeForm.submit.disabled = false;
20   } else {
21     document.QuickChargeForm.submit.disabled = true;
22   }
23
24 % if ( $curuser->option('disable_enter_submit_onetimecharge') ) {
25
26     var key;
27     if (window.event)
28           key = window.event.keyCode; //IE
29     else
30
31           key = e.which; //firefox, others
32
33     return (key != 13);
34
35 % } else {
36     return true;
37 % }
38
39 }
40
41 function validate_quick_charge () {
42   var pkg = document.QuickChargeForm.pkg.value;
43   var pkg_regex = XRegExp('^([\\p{L}\\p{N} \_\!\@\#\$\%\&\(\)\+\;\:\'\"\,\.\?\/\=\\-\\[\\]]*)$');
44   var amount = document.QuickChargeForm.amount.value;
45   var amount_regex = /^\s*\$?\s*(\d*(\.?\d{1,2}))\s*$/ ;
46   var rval = true;
47
48   if ( ! amount_regex.test(amount) ) {
49     alert('Illegal amount - enter an amount to charge, for example, "5" or "43" or "21.46".');
50     return false;
51   }
52   if ( String(pkg).length < 1 ) {
53     rval = false;
54   }
55   if ( ! pkg_regex.test(pkg) ) {
56     rval = false;
57   }
58   var i=0;
59   for (i=0; i < rownum; i++) {
60     if (! eval('pkg_regex.test(document.QuickChargeForm.description' + i + '.value)')){
61       rval = false;
62       break;
63     }
64   }
65   if (rval == true) {
66     return true;
67   }
68
69   if ( ! pkg ) {
70     alert('Enter a description for the one-time charge');
71     return false;
72   }
73
74   alert('Illegal description - spaces, letters, numbers, and the following punctuation characters are allowed: . , ! ? @ # $ % & ( ) - + ; : ' + "'" + ' " = [ ]' );
75   return false;
76 }
77
78 function bill_now_changed (what) {
79   var form = what.form;
80   if ( what.checked ) {
81     form.start_date_text.disabled = true;
82     form.start_date.style.backgroundColor = '#dddddd';
83     form.start_date_button.style.display = 'none';
84     form.start_date_button_disabled.style.display = '';
85     form.invoice_terms.disabled = false;
86   } else {
87     form.start_date_text.disabled = false;
88     form.start_date.style.backgroundColor = '#ffffff';
89     form.start_date_button.style.display = '';
90     form.start_date_button_disabled.style.display = 'none';
91     form.invoice_terms.disabled = true;
92   }
93 }
94
95 </SCRIPT>
96
97 <P>
98 % if ( $cust_main ) {
99 <& /elements/small_custview.html,
100              $cust_main->custnum,
101              scalar($conf->config('countrydefault')),
102              1, #no balance
103 &>
104 % } else {
105 <& /elements/small_prospect_view.html, $prospect_main &>
106 % }
107 </P>
108
109 <FORM ACTION   = "process/quick-charge.cgi"
110       NAME     = "QuickChargeForm"
111       ID       = "QuickChargeForm"
112       METHOD   = "POST"
113       onSubmit = "document.QuickChargeForm.submit.disabled=true; return validate_quick_charge();"
114 >
115
116 <INPUT TYPE="hidden" NAME="custnum"     VALUE="<% $cust_main ? $cust_main->custnum : '' %>">
117 <INPUT TYPE="hidden" NAME="prospectnum" VALUE="<% $prospect_main ? $prospect_main->prospectnum : '' %>">
118 <INPUT TYPE="hidden" NAME="quotationnum" VALUE="<% $quotationnum %>">
119
120 <TABLE ID="QuickChargeTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=0 STYLE="background-color: #cccccc">
121
122 % if ( $cust_pkg ) { #modify one-time charge
123
124 <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $cust_pkg->pkgnum %>">
125 % my $field = '/elements/tr-input-text.html';
126 % # don't allow changing these after the fact
127 % $field = '/elements/tr-fixed.html' if $billed;
128 <& $field,
129   label  => mt('Amount to charge'),
130   field  => 'amount',
131   value  => sprintf('%.2f',$part_pkg->option('setup_fee')),
132   size   => 8,
133   prefix => $money_char,
134 &> 
135
136 % if ( $curuser->access_right('Edit package definition costs') ) {
137   <& $field,
138     label  => mt('Cost'),
139     field  => 'setup_cost',
140     value  => sprintf('%.2f',$part_pkg->setup_cost),
141     size   => 8,
142     prefix => $money_char,
143   &> 
144 % }
145
146 %   if ( $conf->exists('invoice-unitprice') ) {
147 <& $field,
148   label => 'Quantity',
149   field => 'quantity',
150   value => $cust_pkg->quantity
151 &>
152 %   }
153
154 <& /elements/tr-select-pkg_class.html, 'curr_value' => $classnum  &>
155
156 % # crudely estimate whether any agent commission credits might exist
157 %   my @events = grep { $_->part_event->action =~ /credit/ }
158 %                $cust_pkg->cust_event;
159 %   if ( scalar @events ) {
160 <TR><TD></TD>
161   <TD><INPUT TYPE="checkbox" NAME="adjust_commission" VALUE="Y" CHECKED>
162 <% emt('Adjust commission credits if necessary') %>
163 </TD>
164 </TR>
165 %   }
166
167 % #display the future or past charge date, but don't allow changes
168 % # XXX we probably _could_ let as-yet unbilled charges be rescheduled, but
169 % # there's no compelling need yet
170 %   if ( $billed ) {
171       <& /elements/tr-fixed-date.html,
172         label => emt('Billed on'),
173         value => $cust_pkg->get('setup')
174       &>
175 %   } else {
176       <& /elements/tr-input-date-field.html,
177         {
178           name    => 'start_date',
179           label   => emt('Will be billed'),
180           value   => $cust_pkg->get('start_date'),
181           format  => $date_format,
182           noinit  => 1,
183         }
184       &>
185
186       <& /elements/tr-checkbox.html,
187         label => emt('Invoice this charge separately'),
188         field => 'separate_bill',
189         value => 'Y',
190         curr_value => $cust_pkg->get('separate_bill'),
191       &>
192       <TR>
193         <TD ALIGN="right"><% mt('Tax exempt') |h %> </TD>
194         <TD><INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y" <% $cgi->param('setuptax') ? 'CHECKED' : '' %>></TD>
195       </TR>
196
197       <& /elements/tr-select-taxclass.html, 'curr_value' => $part_pkg->get('taxclass')  &>
198
199       <& /elements/tr-select-taxproduct.html,
200         'label' => emt('Tax product'),
201         'curr_value' => $part_pkg->get('taxproductnum')
202       &>
203 % }
204
205 % } else { # new one-time charge
206
207     <TR>
208       <TD ALIGN="right"><% mt('Amount to charge') |h %> </TD>
209       <TD>
210         <% $money_char %><INPUT TYPE       = "text"
211                                 NAME       = "amount"
212                                 SIZE       = 8
213                                 VALUE      = "<% $amount %>"
214                                 onChange   = "return enable_quick_charge(event)"
215                                 onKeyPress = "return enable_quick_charge(event)"
216                          >
217       </TD>
218     </TR>
219
220 %   if ( $curuser->access_right('Edit package definition costs') ) {
221       <& /elements/tr-input-text.html,
222            label  => mt('Cost'),
223            field  => 'setup_cost',
224            value  => $setup_cost,
225            size   => 8,
226            prefix => $money_char,
227       &> 
228 %   }
229
230 %   if ( $conf->exists('invoice-unitprice') ) {
231     <TR>
232       <TD ALIGN="right"><% mt('Quantity') |h %> </TD>
233       <TD>
234         <INPUT TYPE       = "text"
235                NAME       = "quantity"
236                SIZE       = 4
237                VALUE      = "<% $quantity %>"
238                onKeyPress = "return enable_quick_charge(event)">
239       </TD>
240     </TR>
241 %   }
242
243 <& /elements/tr-select-pkg_class.html, 'curr_value' => $classnum  &>
244
245 % unless ( $quotationnum ) {
246
247     <TR>
248       <TD ALIGN="right"><% mt('Invoice now') |h %></TD>
249       <TD>
250         <INPUT TYPE  = "checkbox"
251                NAME  = "bill_now"
252                VALUE = "1"
253                <% $cgi->param('bill_now') ? 'CHECKED' : '' %>
254                onClick  = "bill_now_changed(this);"
255                onChange = "bill_now_changed(this);"
256         >
257         <% mt('with terms') |h %> 
258         <& /elements/select-terms.html,
259              'curr_value' => scalar($cgi->param('invoice_terms')),
260              'disabled'   => ( $cgi->param('bill_now') ? 0 : 1 ),
261              'agentnum'   => $cust_or_prospect->agentnum,
262         &>
263       </TD>
264     </TR>
265
266 %   # false laziness w/misc/order_pkg.html
267     <TR>
268       <TD ALIGN="right"><% mt('Charge date') |h %> </TD>
269       <TD>
270         <INPUT TYPE  = "text"
271                NAME  = "start_date"
272                SIZE  = 32
273                ID    = "start_date_text"
274                VALUE = "<% $start_date %>"
275                onKeyPress="return enable_quick_charge(event)"
276                <% $cgi->param('bill_now')
277                     ? 'STYLE = "background-color:#dddddd" DISABLED'
278                     : ''
279                %>
280         >
281         <IMG SRC   = "<%$fsurl%>images/calendar.png"
282              ID    = "start_date_button"
283              TITLE = "<% mt('Select date') |h %>"
284              STYLE = "cursor:pointer<% $cgi->param('bill_now') ? ';display:none' : '' %>"
285         >
286         <IMG SRC   = "<%$fsurl%>images/calendar-disabled.png"
287              ID    = "start_date_button_disabled"
288              <% $cgi->param('bill_now') ? '' : 'STYLE="display:none"' %>
289         >
290         <FONT SIZE=-1>(<% mt('leave blank to charge immediately') |h %>)</FONT>
291       </TD>
292     </TR>
293
294     <SCRIPT TYPE="text/javascript">
295       Calendar.setup({
296         inputField: "start_date_text",
297         ifFormat:   "<% $date_format %>",
298         button:     "start_date_button",
299         align:      "BR"
300       });
301     </SCRIPT>
302
303 <& /elements/tr-checkbox.html,
304   label => emt('Invoice this charge separately'),
305   field => 'separate_bill',
306   value => 'Y'
307 &>
308
309 % }
310
311 % if ( ! $quotationnum && $cust_main->payby =~ /^(CARD|CHEK)$/ ) {
312 %   my $what = lc(FS::payby->shortname($cust_main->payby));
313     <TR>
314       <TD ALIGN="right"><% mt("Disable automatic $what charge") |h %> </TD>
315       <TD COLSPAN=6><INPUT TYPE="checkbox" NAME="no_auto" VALUE="Y"></TD>
316     </TR>
317 % }
318
319 <TR>
320   <TD ALIGN="right"><% mt('Tax exempt') |h %> </TD>
321   <TD><INPUT TYPE="checkbox" NAME="setuptax" VALUE="Y" <% $cgi->param('setuptax') ? 'CHECKED' : '' %>></TD>
322 </TR>
323
324 <& /elements/tr-select-taxclass.html, 'curr_value' => scalar($cgi->param('taxclass')) &>
325
326 <& /elements/tr-select-taxproduct.html,
327   'label'      => emt('Tax product'),
328   'curr_value' => scalar($cgi->param('taxproductnum')),
329 &>
330
331 <& /elements/tr-select-taxoverride.html,
332   'curr_value' => scalar($cgi->param('tax_override')),
333 &>
334
335 % } # if !$cust_pkg
336
337 <TR>
338   <TD ALIGN="right"><% mt('Description') |h %> </TD>
339   <TD>
340     <INPUT TYPE       = "text"
341            NAME       = "pkg"
342            SIZE       = "50"
343            MAXLENGTH  = "50"
344            VALUE      = "<% $pkg %>"
345            onChange   = "return enable_quick_charge(event)"
346            onKeyPress = "return enable_quick_charge(event)"
347     >
348   </TD>
349 </TR>
350
351 % my $row = 0;
352 % # quotation details are handled by quotation_pkg_detail records, added via link from view/quotation.html
353 % # the details below get attached to the part_pkg record, and there's no way to edit that from quotations
354 % unless ($quotationnum) {
355 <TR>
356   <TD></TD>
357   <TD><FONT SIZE="-1"><% mt('Optional additional description (also printed on invoice):') |h %> </FONT></TD>
358 </TR>
359
360 %   foreach (@description) {
361     <TR>
362       <TD></TD>
363       <TD>
364         <INPUT TYPE       = "text"
365                NAME       = "description<% $row %>"
366                SIZE       = "60"
367                MAXLENGTH  = "65"
368                VALUE      = "<% $_ |h %>"
369                rownum     = "<% $row %>"
370                onKeyPress = "return enable_quick_charge(event)"
371                onKeyUp    = "return possiblyAddRow(event)"
372         >
373       </TD>
374     </TR>
375 %     $row++;
376 %   }
377 % }
378
379
380 </TABLE>
381
382 <BR>
383 % my $label = $cust_pkg
384 %             ? emt('Modify one-time charge')
385 %             : emt('Add one-time charge');
386 <INPUT TYPE="submit" ID="submit" NAME="submit" VALUE="<% $label %>" \
387 <% ($cgi->param('error') || $cust_pkg) ? '' :' DISABLED' %>>
388
389 </FORM>
390
391
392 <SCRIPT TYPE="text/javascript">
393
394   var rownum = <% $row %>;
395
396   function possiblyAddRow(e) {
397
398     if ( ( rownum - this.getAttribute('rownum') ) == 1 ) {
399       addRow();
400     }
401
402 %   if ( $curuser->option('disable_enter_submit_onetimecharge') ) {
403
404       var key;
405       if (window.event)
406             key = window.event.keyCode; //IE
407       else
408             key = e.which; //firefox, others
409
410       return (key != 13);
411
412 %   } else {
413       return true;
414 %   }
415
416   }
417
418   function addRow() {
419
420     var table = document.getElementById('QuickChargeTable');
421     var tablebody = table.getElementsByTagName('tbody').item(0);
422
423     var row = document.createElement('TR');
424
425     var empty_cell = document.createElement('TD');
426     row.appendChild(empty_cell);
427
428     var description_cell = document.createElement('TD');
429
430       //var description_input = document.createElement('INPUT');
431       var di = document.createElement('INPUT');
432       di.setAttribute('name', 'description'+rownum);
433       di.setAttribute('id',   'description'+rownum);
434       di.setAttribute('size', 60);
435       di.setAttribute('maxLength', 65);
436       di.setAttribute('rownum',   rownum);
437       di.onkeyup = possiblyAddRow;
438       di.onkeypress = enable_quick_charge;
439       description_cell.appendChild(di);
440
441     row.appendChild(description_cell);
442
443     tablebody.appendChild(row);
444
445     rownum++;
446
447   }
448
449 </SCRIPT>
450
451 </BODY>
452 </HTML>
453 <%init>
454
455 my $curuser = $FS::CurrentUser::CurrentUser;
456
457 die "access denied"
458   unless $curuser->access_right('One-time charge');
459
460 my $conf = new FS::Conf;
461 my $date_format = $conf->config('date_format') || '%m/%d/%Y';
462 my $money_char = $conf->config('money_char') || '$';
463
464 my( $cust_main, $cust_pkg, $prospect_main, $quotationnum ) = ( '', '', '', '' );
465 if ( $cgi->param('change_pkgnum') ) {
466   # change an existing one-time charge
467   die "access denied"
468     unless $curuser->access_right('Modify one-time charge');
469
470   $cgi->param('change_pkgnum') =~ /^(\d+)$/ or die "illegal pkgnum";
471   $cust_pkg = FS::cust_pkg->by_key($1) or die "pkgnum $1 not found";
472   $cust_main = $cust_pkg->cust_main;
473 } else {
474   if ( $cgi->param('quotationnum') =~ /^(\d+)$/ ) {
475     $quotationnum = $1;
476   }
477   if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
478     $cust_main = FS::cust_main->by_key($1) or die "custnum $1 not found";
479   }
480   if ( $cgi->param('prospectnum') =~ /^(\d+)$/ ) {
481     $prospect_main = FS::prospect_main->by_key($1) or die "prospectnum $1 not found";
482   }
483   die "custnum or prospectnum must be specified"
484     unless $cust_main || $prospect_main;
485 }
486
487 my $cust_or_prospect = $cust_main || $prospect_main;
488
489 if ( $cust_main ) {
490   my $custnum = $cust_main->custnum;
491   # agent-virt
492   if (!exists($curuser->agentnums_href->{$cust_main->agentnum})) {
493     die "custnum $custnum not found";
494   }
495 } elsif ( $prospect_main ) {
496   my $prospectnum = $prospect_main->prospectnum;
497   # agent-virt
498   if (!exists($curuser->agentnums_href->{$prospect_main->agentnum})) {
499     die "prospectnum $prospectnum not found";
500   }
501 }
502
503 my $format = "%m/%d/%Y %T %z (%Z)"; #false laziness w/REAL_cust_pkg.cgi?
504 my $start_date = $cust_main ? $cust_main->next_bill_date : '';
505 $start_date = $start_date ? time2str($format, $start_date) : '';
506
507 my $amount = '';
508 if ( $cgi->param('amount') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) {
509   $amount = $1;
510 }
511
512 my $setup_cost = '';
513 if ( $cgi->param('setup_cost') =~ /^\s*\$?\s*(\d+(\.\d{1,2})?)\s*$/ ) {
514   $setup_cost = $1;
515 }
516
517 my $quantity = 1;
518 if ( $cgi->param('quantity') =~ /^\s*(\d+)\s*$/ ) {
519   $quantity = $1;
520 }
521
522 $cgi->param('pkg') =~ /^([\w \!\@\#\$\%\&\(\)\-\+\;\:\'\"\,\.\?\/\=\[\]]*)$/ 
523   or die 'illegal description';
524 my $pkg = $1;
525
526 my $default_terms;
527 if ( $cust_main && $cust_main->invoice_terms ) {
528   $default_terms = emt("Customer default ([_1])", $cust_main->invoice_terms);
529 } else {
530   $default_terms =
531     emt( "Default ([_1])",
532          ( $conf->config('invoice_default_terms', $cust_or_prospect->agentnum)
533              || emt('Payable upon receipt')
534          )
535        );
536 }
537
538 my @description;
539 my %param = $cgi->Vars;
540 for (my $i = 0; exists($param{"description$i"}); $i++) {
541   push @description, $param{"description$i"};
542 }
543
544 my $classnum;
545 if ( $cgi->param('classnum') =~ /^(\d+)$/ ) {
546   $classnum = $1;
547 }
548
549 my $part_pkg;
550 my $billed = 0;
551
552 if ( $cust_pkg ) { # set defaults
553   $part_pkg = $cust_pkg->part_pkg;
554   $pkg ||= $part_pkg->pkg;
555   $classnum ||= $part_pkg->classnum;
556   if (!@description) {
557     for (my $i = 0; $i < ($part_pkg->option('additional_count',1) || 0); $i++) 
558     {
559       push @description, $part_pkg->option("additional_info$i",1);
560     }
561   }
562   $billed = $cust_pkg->get('setup') ? 1 : 0;
563 }
564
565 </%init>