1 <style type="text/css">
15 /* #customer_menu .ui-menu-item */
17 background-color: #f8f8f8;
21 /* #customer_menu .ui-menu-item */
22 #customer_menu > li.ui-state-focus {
23 background-color: #f8f8f8;
24 border: 1px solid transparent;
27 #customer_menu > li.ui-state-active {
28 background-color: #f8f8f8;
29 border: 1px solid transparent;
32 #customer_menu > li > a {
33 border-top: 1px solid transparent;
34 border-left: 1px solid transparent;
35 border-right: 1px solid transparent;
40 #customer_menu > li.ui-state-active > a {
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?
48 #customer_menu a.current_show {
51 border-top: 1px solid #7e0079;
52 border-left: 1px solid #7e0079;
53 border-right: 1px solid #7e0079;
54 border-bottom: 2px solid #ffffff;
56 border-bottom-left-radius: 2px;
57 border-bottom-right-radius: 2px;
64 border-top-left-radius: .5em;
65 border-top-right-radius: .5em;
71 text-decoration: none;
76 border: 1px solid #7e0079;
78 box-shadow: #333333 1px 1px 2px;
81 #customer_menu ul li {
89 background: transparent;
92 #customer_menu li.ui-menu-divider {
93 border-color: #7e0079;
98 #customer_menu a:hover {
99 text-decoration: underline;
103 #customer_menu ul li.ui-state-focus {
104 background: transparent;
105 border: 1px solid transparent;
110 #customer_menu ul li.ui-state-active {
112 border: 1px solid #7e0079;
118 #customer_menu a .arrow {
120 background-image: url("<% $p %>images/arrow.right.black.png");
126 @-moz-document url-prefix() {
127 #customer_menu a .arrow {
134 <ul id="customer_menu">
135 % foreach my $submenu (@processed_menu) {
137 <% shift @$submenu %>
139 <ul class="customer_submenu">
140 % foreach my $link ( @$submenu ) {
149 <script type="text/javascript">
151 $("#customer_menu").menu({
152 position: { my: "left top", at: "left+1 bottom+3" },
154 $(this).menu("option", "position", { my:"left top", at:"left+1 bottom+3" } );
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"} );
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;
173 my %payby = map { $_ => 1 } $conf->config('payby');
175 # cached for conditions, to avoid looking it up twice
176 my $invoicing_list_emailonly = $cust_main->invoicing_list_emailonly;
178 # nice declarative menu; should be a parameter to some kind of menu generator
185 label => 'Edit customer',
186 url => "edit/cust_main.cgi?$custnum",
187 acl => 'Edit customer'
190 label => 'Edit contacts',
191 url => "edit/cust_main-contacts.html?$custnum",
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,
210 popup => "misc/suspend_cust.html?custnum=$custnum",
211 acl => 'Suspend customer',
212 condition => sub { shift->unsuspended_pkgs > 0 },
213 actionlabel => 'Confirm Suspension',
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',
225 popup => "misc/cancel_cust.html?custnum=$custnum",
226 acl => 'Cancel customer',
227 condition => sub { shift->ncancelled_pkgs > 0 },
228 actionlabel => 'Confirm Cancellation',
233 popup => "misc/merge_cust.html?custnum=$custnum",
234 acl => 'Merge customer',
235 actionlabel => 'Merge customer',
240 label => 'Refer a new customer',
241 url => "edit/cust_main.cgi?referral_custnum=$custnum",
242 confexists => '!disable_customer_referrals',
250 label => 'View this customer\'s referrals',
251 url => "search/cust_main.cgi?referral_custnum=$custnum",
252 confexists => '!disable_customer_referrals',
254 FS::cust_main->count('referral_custnum = ?', shift->custnum) > 0
258 label => 'View billing events',
259 url => "search/cust_event.html?custnum=$custnum",
260 acl => [ 'Billing event reports',
261 'View customer billing events' ],
264 label => 'Email a notice to this customer',
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);
272 condition => sub { $invoicing_list_emailonly },
273 acl => 'Bulk send customer notices',
283 popup => "edit/cust_main_note.cgi?custnum=$custnum",
284 actionlabel => 'Add note',
285 confexists => '!cust_main-disable_notes',
286 acl => 'Add customer note',
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',
304 confexists => 'ticket_system'
309 label => 'Appointments',
310 show => 'appointments',
311 confexists => 'ticket_system',
312 acl => 'View appointments',
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',
324 label => 'Quotations',
325 show => 'quotations',
328 label => 'Create new quotation',
329 url => "edit/quotation.html?custnum=$custnum",
330 acl => 'Generate quotation',
339 label => 'New qualification',
340 popup => "misc/qual.html?custnum=$custnum",
341 actionlabel => 'New qualification',
345 acl => 'Qualify service',
348 label => 'Order new package',
349 popup => "misc/order_pkg.html?custnum=$custnum",
350 actionlabel => 'Order new package',
354 acl => 'Order customer package',
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',
363 label => 'Move services between packages',
364 popup => "edit/bulk-cust_svc-pkgnum.html?custnum=$custnum",
365 actionlabel => 'Move services',
368 acl => 'Bulk move customer services',
371 label => 'Bulk order and cancel packages',
372 url => "edit/cust_pkg.cgi?$custnum",
373 acl => 'Bulk change customer packages',
381 label => 'Package reports',
382 url => "search/report_cust_pkg.html?custnum=$custnum",
385 label => 'View qualifications',
386 url => "search/qual.cgi?custnum=$custnum",
387 acl => 'Qualify service',
390 label => 'View accounts',
391 url => "search/report_svc_acct.html?custnum=$custnum",
394 label => 'View data usage',
395 popup => "search/report_sqlradius_usage-custnum.html?$custnum",
396 acl => 'Usage: RADIUS sessions',
397 actionlabel => 'Data usage report',
400 condition => sub { shift->num_usage_pkgs > 0 },
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
410 label => 'Payment History',
411 show => 'payment_history',
414 # manual payment entry via edit/cust_pay
415 { label => 'Enter payment',
418 label => 'Enter check payment',
419 popup => "edit/cust_pay.cgi?popup=1;payby=BILL;custnum=$custnum",
420 actionlabel => 'Enter check payment',
423 acl => [ 'Post payment', 'Post check payment' ],
426 label => 'Enter cash payment',
427 popup => "edit/cust_pay.cgi?popup=1;payby=CASH;custnum=$custnum",
428 actionlabel => 'Enter cash payment',
431 acl => [ 'Post payment', 'Post cash payment' ],
434 # label => 'Enter Western Union payment',
435 # popup => "edit/cust_pay.cgi?popup=1;payby=WEST;custnum=$custnum",
436 # actionlabel => 'Enter Western Union payment',
439 # acl => [ 'Post payment', ],
440 ## condition => sub { $payby{WEST} },
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',
448 # acl => [ 'Post payment', ],
449 ## condition => sub { $payby{MCRD} },
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',
457 # acl => [ 'Post payment', ],
458 ## condition => sub { $payby{MCHK} },
463 # realtime payments via payment.cgi
464 { label => 'Process payment',
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} },
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} },
486 label => 'Enter credit',
487 popup => "edit/cust_credit.cgi?custnum=$custnum",
488 actionlabel => 'Enter credit',
490 acl => 'Post credit',
493 label => 'Credit line items',
494 popup => "edit/credit-cust_bill_pkg.html?custnum=$custnum",
495 actionlabel => 'Credit line items',
498 acl => 'Credit line items',
500 FS::cust_bill->count('custnum = ?', shift->custnum) > 0
508 { label => 'Enter refund',
512 label => 'Enter check refund',
513 popup => "edit/cust_refund.cgi?popup=1;payby=BILL;custnum=$custnum",
514 actionlabel => 'Enter check refund',
516 acl => ['Post refund', 'Post check refund'],
519 label => 'Enter cash refund',
520 popup => "edit/cust_refund.cgi?popup=1;payby=CASH;custnum=$custnum",
521 actionlabel => 'Enter cash refund',
523 acl => ['Post refund', 'Post cash refund'],
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',
530 # acl => ['Post refund' ],
531 ## condition => sub { $payby{MCRD} },
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',
538 # acl => ['Post refund' ],
539 ## condition => sub { $payby{MCHK} },
551 label => 'Add tax adjustment',
552 popup => "edit/cust_tax_adjustment.html?custnum=$custnum",
553 actionlabel => 'Add tax adjustment',
555 confexists => 'enable_tax_adjustments',
556 acl => 'Add customer tax adjustment',
561 confexists => 'enable_tax_adjustments',
562 acl => 'Add customer tax adjustment',
566 label => 'Email statement to this customer',
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);
574 condition => sub { $invoicing_list_emailonly },
575 acl => 'Resend invoices',
578 label => 'Download PDF statement',
579 url => "view/cust_main_statement-pdf.cgi?$custnum",
580 acl => 'View legacy typeset statements',
582 FS::cust_bill->count('custnum = ?', shift->custnum) > 0
586 label => 'Search invoices',
587 url => "search/report_cust_bill.html?custnum=$custnum",
588 acl => 'List invoices',
591 label => 'View tax exemptions',
592 url => "search/cust_tax_exempt_pkg.cgi?custnum=$custnum",
593 acl => 'View customer tax exemptions',
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',
602 label => 'View pending payments',
603 url => "search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=$custnum",
604 acl => 'View pending payments',
606 FS::cust_pay_pending->count('custnum = ?', shift->custnum) > 0
613 label => 'Change History',
614 show => 'change_history',
615 acl => 'View customer history',
622 foreach my $submenu (@menu) {
626 foreach my $entry ( @$submenu ) {
627 # if the menu head was skipped, skip the whole menu
628 last if (!$first and !@links);
631 my $a = entry2link($entry, $cust_main, $opt{show});
632 push @links, $a if length($a);
637 push @processed_menu, \@links;
643 my( $entry, $cust_main, $show ) = @_;
646 if ( $entry->{acl} ) {
648 unless $FS::CurrentUser::CurrentUser->access_right( $entry->{acl} );
650 if ( $entry->{confexists} ) {
651 if ( $entry->{confexists} =~ /^!(.*)/ ) {
652 # confexists => !foo, a negative condition
653 return '' if FS::Conf->new->exists( $1 );
655 return '' unless FS::Conf->new->exists( $entry->{confexists} );
658 if ( $entry->{condition} ) {
659 return '' unless &{ $entry->{condition} }($cust_main);
662 my $label = emt($entry->{label});
664 if ( $entry->{submenu} ) {
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>';
677 my $target = $entry->{content}
681 if ( ref($target) eq 'CODE' ) {
682 $target = &$target($cust_main);
685 return $target if $entry->{content}; #the coderef specified the whole thing
687 if ( $entry->{show} ) {
689 # the menu head: always a link back to this page
690 $cgi->param('show', $entry->{show});
691 $target = $cgi->self_url;
692 $cgi->param('show', $show);
694 my $a = qq[ <A HREF="$target"];
695 $a .= ' class="current_show"' if $show eq $entry->{show};
696 return $a. qq[>$label</A> ];
698 } elsif ( $entry->{popup} ) {
700 $target =~ s/\$custnum/$custnum/g;
701 $target = $p.$target;
703 return include('/elements/popup_link.html',
711 } elsif ( $entry->{url} ) {
713 $target =~ s/\$custnum/$custnum/g;
714 $target = $p.$target;
716 return qq[ <A HREF="$target">$label</A> ];
719 die "bad entry ". join(',',%$entry). " in menu: no url, popup or content";