Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorIvan Kohler <ivan@freeside.biz>
Sat, 4 May 2013 07:21:14 +0000 (00:21 -0700)
committerIvan Kohler <ivan@freeside.biz>
Sat, 4 May 2013 07:21:14 +0000 (00:21 -0700)
FS/FS/AccessRight.pm
FS/FS/access_right.pm
FS/FS/cust_main/Packages.pm
FS/FS/cust_pkg.pm
httemplate/misc/cust_main-merge.html
httemplate/misc/merge_cust.html
httemplate/view/cust_main.cgi
httemplate/view/cust_main/packages/contact.html
httemplate/view/cust_main/packages/section.html
httemplate/view/cust_main/packages/status.html

index bfb39b4..373617e 100644 (file)
@@ -132,6 +132,7 @@ tie my %rights, 'Tie::IxHash',
     'Order customer package',
     'One-time charge',
     'Change customer package',
+    'Detach customer package',
     'Bulk change customer packages',
     'Edit customer package dates',
     'Discount customer package', #NEW
index d370ba5..5bcf922 100644 (file)
@@ -232,6 +232,7 @@ sub _upgrade_data { # class method
     'Financial reports' => [ 'Employees: Commission Report',
                              'Employees: Audit Report',
                            ],
+    'Change customer package' => 'Detach customer package',
 ;
 
   foreach my $old_acl ( keys %onetime ) {
index f83bce9..41ef228 100644 (file)
@@ -4,9 +4,11 @@ use strict;
 use vars qw( $DEBUG $me );
 use List::Util qw( min );
 use FS::UID qw( dbh );
-use FS::Record qw( qsearch );
+use FS::Record qw( qsearch qsearchs );
 use FS::cust_pkg;
 use FS::cust_svc;
+use FS::contact;       # for attach_pkgs
+use FS::cust_location; #
 
 $DEBUG = 0;
 $me = '[FS::cust_main::Packages]';
@@ -291,6 +293,108 @@ sub order_pkgs {
   ''; #no error
 }
 
+=item attach_pkgs 
+
+Merges this customer's package's into the target customer and then cancels them.
+
+=cut
+
+sub attach_pkgs {
+  my( $self, $new_custnum ) = @_;
+
+  #mostly false laziness w/ merge
+
+  return "Can't attach packages to self" if $self->custnum == $new_custnum;
+
+  my $new_cust_main = qsearchs( 'cust_main', { 'custnum' => $new_custnum } )
+    or return "Invalid new customer number: $new_custnum";
+
+  return 'Access denied: "Merge customer across agents" access right required to merge into a customer of a different agent'
+    if $self->agentnum != $new_cust_main->agentnum 
+    && ! $FS::CurrentUser::CurrentUser->access_right('Merge customer across agents');
+
+  local $SIG{HUP} = 'IGNORE';
+  local $SIG{INT} = 'IGNORE';
+  local $SIG{QUIT} = 'IGNORE';
+  local $SIG{TERM} = 'IGNORE';
+  local $SIG{TSTP} = 'IGNORE';
+  local $SIG{PIPE} = 'IGNORE';
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  if ( qsearch('agent', { 'agent_custnum' => $self->custnum } ) ) {
+     $dbh->rollback if $oldAutoCommit;
+     return "Can't merge a master agent customer";
+  }
+
+  #use FS::access_user
+  if ( qsearch('access_user', { 'user_custnum' => $self->custnum } ) ) {
+     $dbh->rollback if $oldAutoCommit;
+     return "Can't merge a master employee customer";
+  }
+
+  if ( qsearch('cust_pay_pending', { 'custnum' => $self->custnum,
+                                     'status'  => { op=>'!=', value=>'done' },
+                                   }
+              )
+  ) {
+     $dbh->rollback if $oldAutoCommit;
+     return "Can't merge a customer with pending payments";
+  }
+
+  #end of false laziness
+
+  #pull in contact
+
+  my %contact_hash = ( 'first'    => $self->first,
+                       'last'     => $self->get('last'),
+                       'custnum'  => $new_custnum,
+                       'disabled' => '',
+                     );
+
+  my $contact = qsearchs(  'contact', \%contact_hash)
+                 || new FS::contact   \%contact_hash;
+  unless ( $contact->contactnum ) {
+    my $error = $contact->insert;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  foreach my $cust_pkg ( $self->ncancelled_pkgs ) {
+
+    my $cust_location = $cust_pkg->cust_location || $self->ship_location;
+    my %loc_hash = $cust_location->hash;
+    $loc_hash{'locationnum'} = '';
+    $loc_hash{'custnum'}     = $new_custnum;
+    $loc_hash{'disabled'}    = '';
+    my $new_cust_location = qsearchs(  'cust_location', \%loc_hash)
+                             || new FS::cust_location   \%loc_hash;
+
+    my $pkg_or_error = $cust_pkg->change( {
+      'keep_dates'    => 1,
+      'cust_main'     => $new_cust_main,
+      'contactnum'    => $contact->contactnum,
+      'cust_location' => $new_cust_location,
+    } );
+
+    my $error = ref($pkg_or_error) ? '' : $pkg_or_error;
+
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  ''; #no error
+
+}
+
 =item all_pkgs [ OPTION => VALUE... | EXTRA_QSEARCH_PARAMS_HASHREF ]
 
 Returns all packages (see L<FS::cust_pkg>) for this customer.
index d8b6e69..4464aa5 100644 (file)
@@ -1827,7 +1827,7 @@ sub change {
   my $custnum = $self->custnum;
   if ( $opt->{cust_main} ) {
     my $cust_main = $opt->{cust_main};
-    unless ( $cust_main->custnum) { 
+    unless ( $cust_main->custnum ) { 
       my $error = $cust_main->insert;
       if ( $error ) {
         $dbh->rollback if $oldAutoCommit;
@@ -1837,6 +1837,8 @@ sub change {
     $custnum = $cust_main->custnum;
   }
 
+  $hash{'contactnum'} = $opt->{'contactnum'} if $opt->{'contactnum'};
+
   # Create the new package.
   my $cust_pkg = new FS::cust_pkg {
     custnum        => $custnum,
index 4decbef..3b4425f 100755 (executable)
@@ -31,7 +31,17 @@ if ( $cgi->param('new_custnum') =~ /^(\d+)$/ ) {
   } );
   die "No customer # $custnum" unless $cust_main;
 
-  $error = $cust_main->merge($new_custnum);
+  if ( $cgi->param('merge') eq 'Y' ) {
+
+    #old-style merge: everything + delete old customer
+    $error = $cust_main->merge($new_custnum);
+
+  } else {
+
+    #new-style attach: move packages 3.0 style, that's it
+    $error = $cust_main->attach_pkgs($new_custnum);
+
+  }
 
 } else {
   $error = 'Select a customer to merge into';
index ad075be..9c869fa 100644 (file)
@@ -1,6 +1,6 @@
-<% include('/elements/header-popup.html', 'Merge customer' ) %>
+<& /elements/header-popup.html, 'Merge customer' &>
 
-<% include('/elements/error.html') %>
+<& /elements/error.html &>
 
 <FORM NAME="cust_merge_popup" ID="cust_merge_popup" ACTION="<% popurl(1) %>cust_main-merge.html" METHOD=POST onSubmit="submit_merge(); return false;">
 
@@ -35,13 +35,43 @@ function do_submit_merge() {
 <INPUT TYPE="hidden" NAME="custnum" VALUE="<% $custnum %>">
 
 <TABLE BORDER="0" CELLSPACING="2" STYLE="margin-left:auto; margin-right:auto">
-  <% include('/elements/tr-search-cust_main.html',
+
+  <& /elements/tr-search-cust_main.html,
                'label'       => 'Merge into: ',
                'field'       => 'new_custnum',
                'find_button' => 1,
                'curr_value'  => scalar($cgi->param('new_custnum')),
-            )
-  %>
+  &>
+
+% if ( $conf->exists('deletecustomers') ) {
+
+%   if ( scalar($cust_main->ncancelled_pkgs) ) {
+      <TR>
+        <TD COLSPAN=2>
+          <& /elements/radio.html,
+               'field'       => 'merge',
+               'value'       => '',
+               'curr_value'  => scalar($cgi->param('merge')),
+          &>
+          Merge packages only.
+        </TD>
+      </TR>
+%   } else {
+%     $cgi->param('merge', 'Y');
+%   }
+
+    <TR>
+      <TD COLSPAN=2>
+        <& /elements/radio.html,
+             'field'       => 'merge',
+             'value'       => 'Y',
+             'curr_value'  => scalar($cgi->param('merge')),
+        &>
+        Merge invoices, payments/credits, notes, tickets and delete this customer.
+      </TD>
+    </TR>
+% }
+
 </TABLE>
 
 <P ALIGN="CENTER">
@@ -54,6 +84,8 @@ function do_submit_merge() {
 
 <%init>
 
+my $conf = new FS::Conf;
+
 $cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
 my $custnum = $1;
 
index ec31919..be0100f 100755 (executable)
@@ -91,14 +91,19 @@ function areyousure(href, message) {
   &> | 
 % }
 
-% if ( $curuser->access_right('Merge customer') ) {
+% if (     $curuser->access_right('Merge customer')
+%      and (    scalar($cust_main->ncancelled_pkgs)
+%            || $conf->exists('deletecustomers')
+%          )
+%    )
+% {
   <& /elements/popup_link-cust_main.html,
               { 'action'      => $p. 'misc/merge_cust.html',
                 'label'       => emt('Merge this customer'),
                 'actionlabel' => emt('Merge customer'),
                 'cust_main'   => $cust_main,
-                'width'       => 480,
-                'height'      => 192,
+                'width'       => 569,
+                'height'      => 210,
               }
   &> | 
 % } 
index 4e0551b..fe8b715 100644 (file)
@@ -1,15 +1,19 @@
 % if ( $contact ) {
     <% $contact->line |h %>
-% if ( $show_link ) {
+% if ( $show_change_link ) {
         <FONT SIZE=-1>
           (&nbsp;<%pkg_change_contact_link($cust_pkg)%>&nbsp;)
-          (&nbsp;<%pkg_detach_link($cust_pkg)%>&nbsp;)
         </FONT>
-%    }
-% } elsif ( $show_link ) {
+% }
+% if ( $show_detach_link ) {
         <FONT SIZE=-1>
-          (&nbsp;<%pkg_add_contact_link($cust_pkg)%>&nbsp;)
+          (&nbsp;<%pkg_detach_link($cust_pkg)%>&nbsp;)
         </FONT>
+%    }
+% } elsif ( $show_contact_link ) {
+    <FONT SIZE=-1>
+      (&nbsp;<%pkg_add_contact_link($cust_pkg)%>&nbsp;)
+    </FONT>
 % }
 <%init>
 
@@ -18,10 +22,18 @@ my %opt = @_;
 
 my $cust_pkg       = $opt{'cust_pkg'};
 
-my $show_link = 
+my $show_change_link = 
   ! $cust_pkg->get('cancel')
   && $FS::CurrentUser::CurrentUser->access_right('Change customer package');
 
+my $show_detach_link =
+  ! $cust_pkg->get('cancel')
+  && $FS::CurrentUser::CurrentUser->access_right('Detach customer package');
+
+my $show_contact_link =
+  ! $cust_pkg->get('cancel')
+  ; #&& $FS::CurrentUser::CurrentUser->access_right('Add package contact'); #or something like that
+
 my $contact = $cust_pkg->contact_obj;
 
 sub pkg_change_contact_link {
@@ -43,7 +55,7 @@ sub pkg_add_contact_link {
   include( '/elements/popup_link-cust_pkg.html',
     'action'      => $p. "misc/change_pkg_contact.html",
     'label'       => emt('Add contact'),
-    'actionlabel' => emt('Change'),
+    'actionlabel' => emt('Add contact'),
     'cust_pkg'    => $cust_pkg,
     'width'       => 616,
     'height'      => 192,
@@ -59,7 +71,7 @@ sub pkg_detach_link {
     'actionlabel' => emt('Detach'),
     'cust_pkg'    => $cust_pkg,
     'width'       => 616,
-    'height'      => 676,
+    'height'      => 684,
   );
 }
 
index 5f54c0a..391a13b 100755 (executable)
@@ -28,7 +28,7 @@
     <& package.html, %iopt &>
     <& status.html,  %iopt &>
     <TD CLASS="inv" BGCOLOR="<% $iopt{bgcolor} %>" WIDTH="20%" VALIGN="top">
-      <& contact.html, %iopt &>
+      <& contact.html, %iopt &><BR>
       <& location.html, %iopt &>
     </TD>
     <& services.html, %iopt &>
@@ -59,6 +59,15 @@ my $packages = $opt{'packages'};
   ( $a->getfield('pkgnum') <=> $b->getfield('pkgnum') )
 } @$packages;
 
+my %change_custnum = map { $_->change_custnum => 1 }
+                       grep { $_->change_custnum }
+                         grep { $_->getfield('cancel') }
+                           @$packages;
+
+my $pkg_attached = ( scalar(keys %change_custnum) == 1
+                       && ! grep { ! $_->getfield('cancel') } @$packages
+                   );
+
 my $countrydefault = scalar($conf->config('countrydefault')) || 'US';  
 
 my %conf_opt = (
@@ -67,6 +76,7 @@ my %conf_opt = (
                                  || $curuser->option('cust_pkg-display_times')),
   #for status.html
   'cust_pkg-show_autosuspend' => $conf->exists('cust_pkg-show_autosuspend'),
+  'pkg_attached'              => $pkg_attached,
   #for status.html pkg-balances
   'pkg-balances'              => $conf->exists('pkg-balances'),
   'money_char'                => ( $conf->config('money_char') || '$' ),
index 24a4dfc..c0213e9 100644 (file)
@@ -365,9 +365,6 @@ sub pkg_status_row_changed {
 sub pkg_status_row_detached {
   my( $cust_pkg, %opt ) = @_;
 
-warn $cust_pkg->pkgnum;
-warn $cust_pkg->change_custnum;
-
   return '' unless $cust_pkg->change_custnum;
 
   my $html = '';
@@ -379,8 +376,10 @@ warn $cust_pkg->change_custnum;
                       encode_entities( $cust_main->name ).
                     '</A>';
 
+    my $what = $opt{'pkg_attached'} ? 'Attached' : 'Detached';
+
     $html .= pkg_status_row_colspan( $cust_pkg, 
-                                     emt("Detached to customer #[_1]: ",
+                                     emt("$what to customer #[_1]: ",
                                             $cust_pkg->change_custnum
                                         ).
                                        $cust_link,