'Order customer package',
'One-time charge',
'Change customer package',
+ 'Detach customer package',
'Bulk change customer packages',
'Edit customer package dates',
'Discount customer package', #NEW
'Financial reports' => [ 'Employees: Commission Report',
'Employees: Audit Report',
],
+ 'Change customer package' => 'Detach customer package',
;
foreach my $old_acl ( keys %onetime ) {
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]';
''; #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.
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;
$custnum = $cust_main->custnum;
}
+ $hash{'contactnum'} = $opt->{'contactnum'} if $opt->{'contactnum'};
+
# Create the new package.
my $cust_pkg = new FS::cust_pkg {
custnum => $custnum,
} );
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';
-<% 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;">
<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">
<%init>
+my $conf = new FS::Conf;
+
$cgi->param('custnum') =~ /^(\d+)$/ or die 'illegal custnum';
my $custnum = $1;
&> |
% }
-% 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,
}
&> |
% }
% if ( $contact ) {
<% $contact->line |h %>
-% if ( $show_link ) {
+% if ( $show_change_link ) {
<FONT SIZE=-1>
( <%pkg_change_contact_link($cust_pkg)%> )
- ( <%pkg_detach_link($cust_pkg)%> )
</FONT>
-% }
-% } elsif ( $show_link ) {
+% }
+% if ( $show_detach_link ) {
<FONT SIZE=-1>
- ( <%pkg_add_contact_link($cust_pkg)%> )
+ ( <%pkg_detach_link($cust_pkg)%> )
</FONT>
+% }
+% } elsif ( $show_contact_link ) {
+ <FONT SIZE=-1>
+ ( <%pkg_add_contact_link($cust_pkg)%> )
+ </FONT>
% }
<%init>
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 {
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,
'actionlabel' => emt('Detach'),
'cust_pkg' => $cust_pkg,
'width' => 616,
- 'height' => 676,
+ 'height' => 684,
);
}
<& 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 &>
( $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 = (
|| $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') || '$' ),
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 = '';
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,