d12ec172ff7a0fe6d656814ee64e7201790e64ab
[freeside.git] / httemplate / view / cust_main / menu.html
1 <style type="text/css">
2
3 #customer_menu {
4   font-size: smaller;
5   border: none;
6   margin-top: .6em;
7   margin-bottom: 16px;
8 }
9
10 #customer_menu li {
11   float: left;
12   padding: .25em;
13 }
14
15 /* #customer_menu .ui-menu-item  */
16 #customer_menu > li {
17   background-color: #f8f8f8;
18   padding-left: 0px;
19 }
20
21 /* #customer_menu .ui-menu-item  */
22 #customer_menu > li.ui-state-focus {
23   background-color: #f8f8f8;
24   border: 1px solid transparent;
25 }
26
27 #customer_menu > li.ui-state-active {
28   background-color: #f8f8f8;
29   border: 1px solid transparent;
30 }
31
32 #customer_menu > li > a {
33   border-top: 1px solid transparent;
34   border-left: 1px solid transparent;
35   border-right: 1px solid transparent;
36   border-bottom: none;
37   padding: .5em .75em;
38 }
39
40 #customer_menu > li.ui-state-active > a {
41
42 /* if i could find something light enough that didn't look pink?
43      or is this too visually distracting and not the useful hint i think it is?
44   background: #ED55E7;
45 */
46 }
47
48 #customer_menu a.current_show {
49   font-weight: bold;
50   background: #FFFFFF;
51   border-top: 1px solid #7e0079;
52   border-left: 1px solid #7e0079;
53   border-right: 1px solid #7e0079;
54   border-bottom: 2px solid #ffffff;
55   margin-bottom: -2px;
56   border-bottom-left-radius: 2px;
57   border-bottom-right-radius: 2px;
58 }
59
60 #customer_menu a {
61   margin-left: 0;
62   margin-right: 1em;
63   margin-top: 0;
64   border-top-left-radius: .5em;
65   border-top-right-radius: .5em;
66
67   font-weight: normal;
68   background: #e0e0e0;
69   color: #525151;
70   white-space: nowrap;
71   text-decoration: none;
72 }
73
74 #customer_menu ul {
75   background: #ffffff;
76   border: 1px solid #7e0079;
77   border-radius: 2px;
78   box-shadow: #333333 1px 1px 2px;
79 }
80
81 #customer_menu ul li {
82   float: none;
83   margin-right: 2px;
84   margin-left: 2px;
85 }
86
87 #customer_menu ul a {
88   color: #333333;
89   background: transparent;
90 }
91
92 #customer_menu li.ui-menu-divider {
93   border-color: #7e0079;
94 /*  margin-right: 2px;
95   margin-left: 2px; */
96 }
97
98 #customer_menu a:hover {
99   text-decoration: underline;
100   color: #7e0079;
101 }
102
103 #customer_menu ul li.ui-state-focus {
104   background: transparent;
105   border: 1px solid transparent;
106   margin-right: 1px;
107   margin-left: 1px;
108 }
109
110 #customer_menu ul li.ui-state-active {
111   background: #f8f0fc;
112   border: 1px solid #7e0079;
113   border-radius: 2px;
114   margin-right: 1px;
115   margin-left: 1px;
116 }
117
118 #customer_menu a .arrow {
119   float: right;
120   background-image: url("<% $p %>images/arrow.right.black.png");
121   width: 3px;
122   height: 6px;
123   margin-top:4px;
124 }
125
126 @-moz-document url-prefix() {
127   #customer_menu a .arrow {
128     margin-top:-.8em;
129   }
130 }
131
132 </style>
133
134 <ul id="customer_menu">
135 % foreach my $submenu (@processed_menu) {
136   <li>
137     <% shift @$submenu %>
138 %   if ( @$submenu ) {
139       <ul class="customer_submenu">
140 %     foreach my $link ( @$submenu ) {
141         <li><% $link %></li>
142 %     }
143       </ul>
144 %   }
145   </li>
146 % }
147 </ul>
148
149 <script type="text/javascript">
150
151   $("#customer_menu").menu({
152     position: { my: "left top", at: "left+1 bottom+3" },
153     blur: function() {
154       $(this).menu("option", "position", { my:"left top", at:"left+1 bottom+3" } );
155     },
156     focus: function(e,ui) {
157       if ($("#customer_menu").get(0) !== $(ui).get(0).item.parent().get(0)) {
158         $(this).menu("option", "position", { my:"left top", at:"right+2 top"} );
159       }
160     },
161   });
162
163 </script>
164
165
166 <%init>
167 my %opt = @_;
168 my $cust_main = $opt{'cust_main'};
169 my $custnum = $cust_main->custnum;
170 my $curuser = $FS::CurrentUser::CurrentUser;
171 my $conf = FS::Conf->new;
172
173 my %payby = map { $_ => 1 } $conf->config('payby');
174
175 # cached for conditions, to avoid looking it up twice
176 my $invoicing_list_emailonly = $cust_main->invoicing_list_emailonly;
177
178 # nice declarative menu; should be a parameter to some kind of menu generator
179 my @menu = ( 
180   [
181     { show        => 'basics',
182       label       => 'Basics',
183     },
184     {
185       label       => 'Edit customer',
186       url         => "edit/cust_main.cgi?$custnum",
187       acl         => 'Edit customer'
188     },
189     {
190       label       => 'Edit contacts',
191       url         => "edit/cust_main-contacts.html?$custnum",
192     },
193
194     { label   => '-',
195       content => '-',
196     },
197
198     {
199       label       => 'Bill now',
200       acl         => 'Bill customer now',
201       content     => sub { include( '/elements/bill.html',
202                               label   => emt('Bill now'),
203                               url     => $cgi->self_url,
204                               custnum => shift->custnum,
205                            ),
206                      },
207     },
208     {
209       label       => 'Suspend',
210       popup       => "misc/suspend_cust.html?custnum=$custnum",
211       acl         => 'Suspend customer',
212       condition   => sub { shift->unsuspended_pkgs > 0 },
213       actionlabel => 'Confirm Suspension',
214       color       => '#ff9900',
215     },
216     {
217       label       => 'Unsuspend',
218       popup       => "misc/unsuspend_cust.html?custnum=$custnum",
219       acl         => 'Unsuspend customer',
220       condition   => sub { shift->suspended_pkgs > 0 },
221       actionlabel => 'Confirm Unsuspension',
222     },
223     {
224       label       => 'Cancel',
225       popup       => "misc/cancel_cust.html?custnum=$custnum",
226       acl         => 'Cancel customer',
227       condition   => sub { shift->ncancelled_pkgs > 0 },
228       actionlabel => 'Confirm Cancellation',
229       color       => '#ff0000',
230     },
231     {
232       label       => 'Merge',
233       popup       => "misc/merge_cust.html?custnum=$custnum",
234       acl         => 'Merge customer',
235       actionlabel => 'Merge customer',
236       width       => 569,
237       height      => 210,
238     },
239     {
240       label       => 'Refer a new customer',
241       url         => "edit/cust_main.cgi?referral_custnum=$custnum",
242       confexists  => '!disable_customer_referrals',
243     },
244
245     { label   => '-',
246       content => '-',
247     },
248
249     {
250       label       => 'View this customer\'s referrals',
251       url         => "search/cust_main.cgi?referral_custnum=$custnum",
252       confexists  => '!disable_customer_referrals',
253       condition   => sub {
254         FS::cust_main->count('referral_custnum = ?', shift->custnum) > 0
255       },
256     },
257     {
258       label       => 'View billing events',
259       url         => "search/cust_event.html?custnum=$custnum",
260       acl         => [ 'Billing event reports',
261                        'View customer billing events' ],
262     },
263     {
264       label       => 'Email a notice to this customer',
265       url         => sub {
266                       my $cust_main = shift;
267                       my $agentnum = $cust_main->agentnum;
268                       'misc/email-customers.html?table=cust_main;'.
269                       'agent_virt_agentnum='.$agentnum.";custnum=$custnum;url=".
270                       uri_escape($cgi->self_url);
271                      },
272       condition   => sub { $invoicing_list_emailonly },
273       acl         => 'Bulk send customer notices',
274     },
275   ],
276   [
277     {
278       label => 'Notes',
279       show  => 'notes',
280     },
281     {
282       label       => 'Add note',
283       popup       => "edit/cust_main_note.cgi?custnum=$custnum",
284       actionlabel => 'Add note',
285       confexists  => '!cust_main-disable_notes',
286       acl         => 'Add customer note',
287       width       => 875,
288       height      => 538,
289     },
290     {
291       label       => 'Attach file',
292       popup       => "edit/cust_main_attach.cgi?custnum=$custnum",
293       actionlabel => 'Upload file',
294       confexists  => '!disable_cust_attachment',
295       acl         => 'Add attachment',
296       width       => 480,
297       height      => 296,
298     },
299   ],
300   [
301     {
302       label => 'Tickets',
303       show  => 'tickets',
304       confexists => 'ticket_system'
305     },
306   ],
307   [
308     {
309       label => 'Appointments',
310       show  => 'appointments',
311       confexists => 'ticket_system',
312       acl   => 'View appointments',
313     },
314     {
315       label       => 'Schedule new appointment',
316       confexists  => 'ticket_system',
317       acl        => 'Make appointment',
318       popup       => "misc/make_appointment.html?custnum=$custnum",
319       actionlabel => 'Schedule appointment',
320     },
321   ],
322   [
323     {
324       label => 'Quotations',
325       show  => 'quotations',
326     },
327     {
328       label => 'Create new quotation',
329       url   => "edit/quotation.html?custnum=$custnum",
330       acl   => 'Generate quotation',
331     },
332   ],
333   [
334     {
335       label => 'Packages',
336       show  => 'packages',
337     },
338     {
339       label       => 'New qualification',
340       popup       => "misc/qual.html?custnum=$custnum",
341       actionlabel => 'New qualification',
342       color       => '#333399',
343       width       => 763,
344       height      => 436,
345       acl         => 'Qualify service',
346     },
347     {
348       label       => 'Order new package',
349       popup       => "misc/order_pkg.html?custnum=$custnum",
350       actionlabel => 'Order new package',
351       color       => '#333399',
352       width       => 960,
353       height      => 740,
354       acl         => 'Order customer package',
355     },
356     {
357       # it's just a popup, but there's some freaky CCH tax stuff in it
358       label       => 'One-time charge',
359       url         => "edit/quick-charge.html?custnum=$custnum",
360       acl         => 'One-time charge',
361     },
362     {
363       label       => 'Move services between packages',
364       popup       => "edit/bulk-cust_svc-pkgnum.html?custnum=$custnum",
365       actionlabel => 'Move services',
366       width       => 968,
367       height      => 575,
368       acl         => 'Bulk move customer services',
369     },
370     {
371       label       => 'Bulk order and cancel packages',
372       url         => "edit/cust_pkg.cgi?$custnum",
373       acl         => 'Bulk change customer packages',
374     },
375
376     { label   => '-',
377       content => '-',
378     },
379
380     {
381       label => 'Package reports',
382       url   => "search/report_cust_pkg.html?custnum=$custnum",
383     },
384     {
385       label => 'View qualifications',
386       url   => "search/qual.cgi?custnum=$custnum",
387       acl   => 'Qualify service',
388     },
389     {
390       label => 'View accounts',
391       url   => "search/report_svc_acct.html?custnum=$custnum",
392     },
393     {
394       label => 'View data usage',
395       popup => "search/report_sqlradius_usage-custnum.html?$custnum",
396       acl   => 'Usage: RADIUS sessions',
397       actionlabel => 'Data usage report',
398       width   => 480,
399       height  => 345,
400       condition => sub { shift->num_usage_pkgs > 0 },
401     },
402     {
403       label => 'View CDRs',
404       url   => "search/report_cdr.html?custnum=$custnum",
405       # XXX should have a condition that the customer has any CDR packages
406     },
407   ],
408   [
409     {
410       label => 'Payment History',
411       show  => 'payment_history',
412     },
413
414     # manual payment entry via edit/cust_pay
415     { label   => 'Enter payment',
416       submenu => [
417         {
418           label       => 'Enter check payment',
419           popup       => "edit/cust_pay.cgi?popup=1;payby=BILL;custnum=$custnum",
420           actionlabel => 'Enter check payment',
421           width       => 763,
422           height      => 392,
423           acl         => [ 'Post payment', 'Post check payment' ],
424         },
425         {
426           label       => 'Enter cash payment',
427           popup       => "edit/cust_pay.cgi?popup=1;payby=CASH;custnum=$custnum",
428           actionlabel => 'Enter cash payment',
429           width       => 763,
430           height      => 392,
431           acl         => [ 'Post payment', 'Post cash payment' ],
432         },
433         #{
434         #  label       => 'Enter Western Union payment',
435         #  popup       => "edit/cust_pay.cgi?popup=1;payby=WEST;custnum=$custnum",
436         #  actionlabel => 'Enter Western Union payment',
437         #  width       => 763,
438         #  height      => 392,
439         #  acl         => [ 'Post payment', ],
440         ##  condition   => sub { $payby{WEST} },
441         #},
442         #{
443         #  label       => 'Record manual (offline/POS) credit card payment',
444         #  popup       => "edit/cust_pay.cgi?popup=1;payby=MCRD;custnum=$custnum",
445         #  actionlabel => 'Enter credit card payment',
446         #  width       => 763,
447         #  height      => 392,
448         #  acl         => [ 'Post payment', ],
449         ##  condition   => sub { $payby{MCRD} },
450         #},
451         #{
452         #  label       => 'Record manual (offline/POS) electronic check',
453         #  popup       => "edit/cust_pay.cgi?popup=1;payby=MCHK;custnum=$custnum",
454         #  actionlabel => 'Enter credit card payment',
455         #  width       => 763,
456         #  height      => 392,
457         #  acl         => [ 'Post payment', ],
458         ##  condition   => sub { $payby{MCHK} },
459         #},
460       ],
461     },
462
463     # realtime payments via payment.cgi
464     { label   => 'Process payment',
465       submenu => [
466         {
467           label       => 'Process credit card payment',
468           url         => "misc/payment.cgi?payby=CARD;custnum=$custnum",
469           acl         => [ 'Process payment', 'Process credit card payment', ],
470           condition   => sub { $payby{CARD} or $payby{DCRD} },
471         },
472         {
473           label       => 'Process electronic check payment',
474           url         => "misc/payment.cgi?payby=CHEK;custnum=$custnum",
475           acl         => [ 'Process payment', 'Process Echeck payment', ],
476           condition   => sub { $payby{CHEK} or $payby{DCHK} },
477         },
478       ],
479     },
480
481     { label   => '-',
482       content => '-',
483     },
484
485     {
486       label       => 'Enter credit',
487       popup       => "edit/cust_credit.cgi?custnum=$custnum",
488       actionlabel => 'Enter credit',
489       width       => 763,
490       acl         => 'Post credit',
491     },
492     {
493       label       => 'Credit line items',
494       popup       => "edit/credit-cust_bill_pkg.html?custnum=$custnum",
495       actionlabel => 'Credit line items',
496       width       => 968,
497       height      => 575,
498       acl         => 'Credit line items',
499       condition   => sub {
500         FS::cust_bill->count('custnum = ?', shift->custnum) > 0
501       },
502     },
503
504     { label   => '-',
505       content => '-',
506     },
507
508     { label   => 'Enter refund',
509       submenu => [
510
511         {
512           label       => 'Enter check refund',
513           popup       => "edit/cust_refund.cgi?popup=1;payby=BILL;custnum=$custnum",
514           actionlabel => 'Enter check refund',
515           width       => 440,
516           acl         => ['Post refund', 'Post check refund'],
517         },
518         {
519           label       => 'Enter cash refund',
520           popup       => "edit/cust_refund.cgi?popup=1;payby=CASH;custnum=$custnum",
521           actionlabel => 'Enter cash refund',
522           width       => 392,
523           acl         => ['Post refund', 'Post cash refund'],
524         },
525         #{
526         #  label       => 'Record manual (offline/POS) credit card refund',
527         #  popup       => "edit/cust_refund.cgi?popup=1;payby=MCRD;custnum=$custnum",
528         #  actionlabel => 'Enter credit card refund',
529         #  width       => 440,
530         #  acl         => ['Post refund' ],
531         ##  condition   => sub { $payby{MCRD} },
532         #},
533         #{
534         #  label       => 'Record manual (offline/POS) electronic check refund',
535         #  popup       => "edit/cust_refund.cgi?popup=1;payby=MCHK;custnum=$custnum",
536         #  actionlabel => 'Enter electronic check refund',
537         #  width       => 440,
538         #  acl         => ['Post refund' ],
539         ##  condition   => sub { $payby{MCHK} },
540         #},
541
542       ],
543
544     },
545
546     { label   => '-',
547       content => '-',
548     },
549
550     {
551       label       => 'Add tax adjustment',
552       popup       => "edit/cust_tax_adjustment.html?custnum=$custnum",
553       actionlabel => 'Add tax adjustment',
554       height      => 200,
555       confexists  => 'enable_tax_adjustments',
556       acl         => 'Add customer tax adjustment',
557     },
558
559     { label       => '-',
560       content     => '-',
561       confexists  => 'enable_tax_adjustments',
562       acl         => 'Add customer tax adjustment',
563     },
564
565     {
566       label       => 'Email statement to this customer',
567       url         => sub {
568                       my $cust_main = shift;
569                       my $agentnum = $cust_main->agentnum;
570                       'misc/email-customer-statement.html?table=cust_main;'.
571                       'agent_virt_agentnum='.$agentnum.";custnum=$custnum;url=".
572                       uri_escape($cgi->self_url);
573                      },
574       condition   => sub { $invoicing_list_emailonly },
575       acl         => 'Resend invoices',
576     },
577     {
578       label       => 'Download PDF statement',
579       url         => "view/cust_main_statement-pdf.cgi?$custnum",
580       acl         => 'View legacy typeset statements',
581       condition   => sub {
582         FS::cust_bill->count('custnum = ?', shift->custnum) > 0
583       },
584     },
585     {
586       label       => 'Search invoices',
587       url         => "search/report_cust_bill.html?custnum=$custnum",
588       acl         => 'List invoices',
589     },
590     {
591       label       => 'View tax exemptions',
592       url         => "search/cust_tax_exempt_pkg.cgi?custnum=$custnum",
593       acl         => 'View customer tax exemptions',
594     },
595     {
596       label       => 'View tax adjustments',
597       url         => "search/cust_tax_adjustment.html?custnum=$custnum",
598       confexists  => 'enable_tax_adjustments',
599       acl         => 'Add customer tax adjustment',
600     },
601     {
602       label       => 'View pending payments',
603       url         => "search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=$custnum",
604       acl         => 'View pending payments',
605       condition   => sub { 
606         FS::cust_pay_pending->count('custnum = ?', shift->custnum) > 0
607       },
608     },
609
610   ],
611   [
612     {
613       label => 'Change History',
614       show  => 'change_history',
615       acl   => 'View customer history',
616     },
617   ],
618 );
619
620
621 my @processed_menu;
622 foreach my $submenu (@menu) {
623
624   my @links;
625   my $first = 1;
626   foreach my $entry ( @$submenu ) {
627     # if the menu head was skipped, skip the whole menu
628     last if (!$first and !@links);
629     $first = 0;
630
631     my $a = entry2link($entry, $cust_main, $opt{show});
632     push @links, $a if length($a);
633
634   } # foreach $entry
635
636   if (@links) {
637     push @processed_menu, \@links;
638   }
639
640 }
641
642 sub entry2link {
643     my( $entry, $cust_main, $show ) = @_;
644
645     # check conditions
646     if ( $entry->{acl} ) {
647       return ''
648         unless $FS::CurrentUser::CurrentUser->access_right( $entry->{acl} );
649     }
650     if ( $entry->{confexists} ) {
651       if ( $entry->{confexists} =~ /^!(.*)/ ) {
652         # confexists => !foo, a negative condition
653         return '' if FS::Conf->new->exists( $1 );
654       } else {
655         return '' unless FS::Conf->new->exists( $entry->{confexists} );
656       }
657     }
658     if ( $entry->{condition} ) {
659       return '' unless &{ $entry->{condition} }($cust_main);
660     }
661
662     my $label = emt($entry->{label});
663
664     if ( $entry->{submenu} ) {
665
666       my $a = '<a href="javascript:void(0);">'. $label.
667               '<span class="arrow"></span>'.
668               '</a><ul class="customer_subsubmenu">';
669       foreach my $submenu (@{ $entry->{submenu} }) {
670         $a .= '<li>'. entry2link($submenu, $cust_main, $show), '</li>';
671       }
672
673       return $a. '</ul>';
674
675     }
676
677     my $target = $entry->{content}
678               || $entry->{popup}
679               || $entry->{url};
680
681     if ( ref($target) eq 'CODE' ) {
682       $target = &$target($cust_main);
683     }
684
685     return $target if $entry->{content}; #the coderef specified the whole thing
686
687     if ( $entry->{show} ) {
688
689       # the menu head: always a link back to this page
690       $cgi->param('show', $entry->{show});
691
692       #$target = $cgi->self_url;
693       #XXX support installs outside /freeside in 4.x
694       $target = "/freeside/view/cust_main.cgi?show=$show;custnum=". $cust_main->custnum;
695
696       $cgi->param('show', $show);
697
698       my $a = qq[ <A HREF="$target"];
699       $a .= ' class="current_show"' if $show eq $entry->{show};
700       return $a. qq[>$label</A> ];
701
702     } elsif ( $entry->{popup} ) {
703
704       $target =~ s/\$custnum/$custnum/g;
705       $target = $p.$target;
706
707       return include('/elements/popup_link.html',
708         action  => $target,
709         width   => 616,
710         height  => 410,
711         %$entry,
712         label   => $label,
713       );
714
715     } elsif ( $entry->{url} ) {
716
717       $target =~ s/\$custnum/$custnum/g;
718       $target = $p.$target;
719
720       return qq[ <A HREF="$target">$label</A> ];
721
722     } else {
723       die "bad entry ". join(',',%$entry). " in menu: no url, popup or content";
724     }
725
726 }
727
728 </%init>