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