summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Kohler <ivan@freeside.biz>2015-02-11 11:56:39 -0800
committerIvan Kohler <ivan@freeside.biz>2015-02-11 11:56:39 -0800
commit81bad22dc70a9277331d2d15ff25810f615c4a92 (patch)
tree3200e2907bd58498926c070bd24b7d04b08bef9e
parentb68c4a4a92d9b03527f109235093fcc98b98dd2a (diff)
parenta7d1e60f6b411c9a8daa1f654855440f59bc05ea (diff)
Merge branch 'master' of git.freeside.biz:/home/git/freeside
-rw-r--r--FS/FS/API.pm17
-rw-r--r--FS/FS/Conf.pm10
-rw-r--r--FS/FS/cdr/cia.pm54
-rw-r--r--FS/FS/cdr/cia_callblast.pm54
-rw-r--r--FS/FS/cust_main.pm21
-rw-r--r--FS/FS/cust_pkg.pm46
-rw-r--r--FS/FS/part_pkg/global_Mixin.pm5
-rw-r--r--FS/FS/reason.pm2
-rwxr-xr-xbin/xmlrpc-customer_bill_now21
-rw-r--r--httemplate/elements/change_history_common.html34
-rwxr-xr-xhttemplate/misc/cust_main-cancel.cgi3
-rw-r--r--httemplate/view/cust_main/misc.html7
12 files changed, 221 insertions, 53 deletions
diff --git a/FS/FS/API.pm b/FS/FS/API.pm
index dd172c143..c49fb205a 100644
--- a/FS/FS/API.pm
+++ b/FS/FS/API.pm
@@ -603,8 +603,21 @@ sub location_info {
Bills a single customer now, in the same fashion as the "Bill now" link in the
UI.
-Returns a hash reference with a single key, 'error'. If there is an error,
-the value contains the error, otherwise it is empty.
+Returns a hash reference with a single key, 'error'. If there is an error,
+the value contains the error, otherwise it is empty. Takes a list of keys and
+values as parameters with the following keys:
+
+=over 4
+
+=item secret
+
+API Secret (required)
+
+=item custnum
+
+Customer number (required)
+
+=back
=cut
diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm
index a4e26f7c8..091070ec5 100644
--- a/FS/FS/Conf.pm
+++ b/FS/FS/Conf.pm
@@ -4627,6 +4627,16 @@ and customer address. Include units.',
},
{
+ 'key' => 'part_pkg-delay_cancel-days',
+ 'section' => '',
+ 'description' => 'Expire packages in this many days when using delay_cancel (default is 1)',
+ 'type' => 'text',
+ 'validate' => sub { (($_[0] =~ /^\d*$/) && (($_[0] eq '') || $_[0]))
+ ? 'Must specify an integer number of days'
+ : '' }
+ },
+
+ {
'key' => 'mcp_svcpart',
'section' => '',
'description' => 'Master Control Program svcpart. Leave this blank.',
diff --git a/FS/FS/cdr/cia.pm b/FS/FS/cdr/cia.pm
index 0471e9bb4..ca44c0fdf 100644
--- a/FS/FS/cdr/cia.pm
+++ b/FS/FS/cdr/cia.pm
@@ -1,9 +1,8 @@
package FS::cdr::cia;
use strict;
-use vars qw( @ISA %info $date $tmp_mday $tmp_mon $tmp_year);
+use vars qw( @ISA %info );
use FS::cdr qw(_cdr_date_parser_maker);
-use Time::Local;
@ISA = qw(FS::cdr);
@@ -12,39 +11,26 @@ use Time::Local;
'weight' => 510,
'header' => 1,
'type' => 'csv',
- 'sep_char' => "|",
+ 'sep_char' => "\t",
'import_fields' => [
- 'accountcode',
- skip(2), # First and last name
-
- sub { my($cdr, $date) = @_;
- $date =~ /^(\d{1,2})\/(\d{1,2})\/(\d\d(\d\d)?)$/
- or die "unparsable date: $date"; #maybe we shouldn't die...
- ($tmp_mday, $tmp_mon, $tmp_year) = ( $2, $1-1, $3 );
- }, #Date
-
- sub { my($cdr, $time) = @_;
- #my($sec, $min, $hour, $mday, $mon, $year)= localtime($cdr->startdate);
- $time =~ /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/
- or die "unparsable time: $time"; #maybe we shouldn't die...
- $cdr->startdate( timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year));
- $cdr->answerdate( timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year));
-
- }, # Start time
-
- sub { my($cdr, $time) = @_;
- #my($sec, $min, $hour, $mday, $mon, $year)= localtime($cdr->startdate);
- $time =~ /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/
- or die "unparsable time: $time"; #maybe we shouldn't die...
- #$cdr->startdate( timelocal($3, $2, $1 ,$mday, $mon, $year) );
- $cdr->enddate(
- timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year) );
- }, # End time
-
- 'disposition', # Disposition
- 'dst', # PhoneNumber
- skip(3), # Extension, Service Type, Filler
- 'src', # ClientContactID
+ skip(2), # Reseller Account Number, Confirmation Number
+ 'description', # Conference Name
+ skip(3), # Organization Name, Bill Code, Q&A Active
+ 'userfield', # Chairperson Name
+ skip(2), # Conference Start Time, Conference End Time
+ _cdr_date_parser_maker('startdate'), # Connect Time
+ _cdr_date_parser_maker('enddate'), # Disconnect Time
+ skip(1), # Duration
+ sub { my($cdr, $data, $conf, $param) = @_;
+ $cdr->duration($data);
+ $cdr->billsec( $data);
+ }, # Roundup Duration
+ skip(1), # User Name
+ 'dst', # DNIS
+ 'src', # ANI
+ skip(2), # Call Type, Toll Free,
+ 'accountcode', # Chair Conference Entry Code
+ skip(1), # Participant Conference Entry Code,
],
);
diff --git a/FS/FS/cdr/cia_callblast.pm b/FS/FS/cdr/cia_callblast.pm
new file mode 100644
index 000000000..d59cd7823
--- /dev/null
+++ b/FS/FS/cdr/cia_callblast.pm
@@ -0,0 +1,54 @@
+package FS::cdr::cia_callblast;
+
+use strict;
+use vars qw( @ISA %info $date $tmp_mday $tmp_mon $tmp_year);
+use FS::cdr qw(_cdr_date_parser_maker);
+use Time::Local;
+
+@ISA = qw(FS::cdr);
+
+%info = (
+ 'name' => 'Client Instant Access Callblast',
+ 'weight' => 510,
+ 'header' => 1,
+ 'type' => 'csv',
+ 'sep_char' => "|",
+ 'import_fields' => [
+ 'accountcode',
+ skip(2), # First and last name
+
+ sub { my($cdr, $date) = @_;
+ $date =~ /^(\d{1,2})\/(\d{1,2})\/(\d\d(\d\d)?)$/
+ or die "unparsable date: $date"; #maybe we shouldn't die...
+ ($tmp_mday, $tmp_mon, $tmp_year) = ( $2, $1-1, $3 );
+ }, #Date
+
+ sub { my($cdr, $time) = @_;
+ #my($sec, $min, $hour, $mday, $mon, $year)= localtime($cdr->startdate);
+ $time =~ /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/
+ or die "unparsable time: $time"; #maybe we shouldn't die...
+ $cdr->startdate( timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year));
+ $cdr->answerdate( timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year));
+
+ }, # Start time
+
+ sub { my($cdr, $time) = @_;
+ #my($sec, $min, $hour, $mday, $mon, $year)= localtime($cdr->startdate);
+ $time =~ /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/
+ or die "unparsable time: $time"; #maybe we shouldn't die...
+ #$cdr->startdate( timelocal($3, $2, $1 ,$mday, $mon, $year) );
+ $cdr->enddate(
+ timelocal($3, $2, $1 ,$tmp_mday, $tmp_mon, $tmp_year) );
+ }, # End time
+
+ 'disposition', # Disposition
+ 'dst', # PhoneNumber
+ skip(3), # Extension, Service Type, Filler
+ 'src', # ClientContactID
+ ],
+
+);
+
+sub skip { map {''} (1..$_[0]) }
+
+1;
diff --git a/FS/FS/cust_main.pm b/FS/FS/cust_main.pm
index d38f3d01b..c3b141e1b 100644
--- a/FS/FS/cust_main.pm
+++ b/FS/FS/cust_main.pm
@@ -3900,6 +3900,27 @@ sub cust_status {
}
}
+=item is_status_delay_cancel
+
+Returns true if customer status is 'suspended'
+and all suspended cust_pkg return true for
+cust_pkg->is_status_delay_cancel.
+
+This is not a real status, this only meant for hacking display
+values, because otherwise treating the customer as suspended is
+really the whole point of the delay_cancel option.
+
+=cut
+
+sub is_status_delay_cancel {
+ my ($self) = @_;
+ return 0 unless $self->status eq 'suspended';
+ foreach my $cust_pkg ($self->ncancelled_pkgs) {
+ return 0 unless $cust_pkg->is_status_delay_cancel;
+ }
+ return 1;
+}
+
=item ucfirst_cust_status
=item ucfirst_status
diff --git a/FS/FS/cust_pkg.pm b/FS/FS/cust_pkg.pm
index 3bd210706..1cc83b6a0 100644
--- a/FS/FS/cust_pkg.pm
+++ b/FS/FS/cust_pkg.pm
@@ -823,6 +823,20 @@ sub cancel {
my $date = $options{'date'} if $options{'date'}; # expire/cancel later
$date = '' if ($date && $date <= $cancel_time); # complain instead?
+ my $delay_cancel = undef;
+ if ( !$date && $self->part_pkg->option('delay_cancel',1)
+ && (($self->status eq 'active') || ($self->status eq 'suspended'))
+ ) {
+ my $expdays = $conf->config('part_pkg-delay_cancel-days') || 1;
+ my $expsecs = 60*60*24*$expdays;
+ my $suspfor = $self->susp ? $cancel_time - $self->susp : 0;
+ $expsecs = $expsecs - $suspfor if $suspfor;
+ unless ($expsecs <= 0) { #if it's already been suspended long enough, don't re-suspend
+ $delay_cancel = 1;
+ $date = $cancel_time + $expsecs;
+ }
+ }
+
#race condition: usage could be ongoing until unprovisioned
#resolved by performing a change package instead (which unprovisions) and
#later cancelling
@@ -893,6 +907,11 @@ sub cancel {
my %hash = $self->hash;
if ( $date ) {
$hash{'expire'} = $date;
+ if ($delay_cancel) {
+ $hash{'susp'} = $cancel_time unless $self->susp;
+ $hash{'adjourn'} = undef;
+ $hash{'resume'} = undef;
+ }
} else {
$hash{'cancel'} = $cancel_time;
}
@@ -1643,7 +1662,7 @@ sub unsuspend {
)
or $hash{'order_date'} == $hash{'susp'}
or $self->part_pkg->option('unused_credit_suspend')
- or ( defined($reason) and $reason->unused_credit )
+ or ( ref($reason) and $reason->unused_credit )
) {
$adjust_bill = 0;
}
@@ -3343,6 +3362,31 @@ sub statuscolor {
$statuscolor{$self->status};
}
+=item is_status_delay_cancel
+
+Returns true if part_pkg has option delay_cancel,
+cust_pkg status is 'suspended' and expire is set
+to cancel package within the next day (or however
+many days are set in global config part_pkg-delay_cancel-days.
+
+This is not a real status, this only meant for hacking display
+values, because otherwise treating the package as suspended is
+really the whole point of the delay_cancel option.
+
+=cut
+
+sub is_status_delay_cancel {
+ my ($self) = @_;
+ return 0 unless $self->part_pkg->option('delay_cancel',1);
+ return 0 unless $self->status eq 'suspended';
+ return 0 unless $self->expire;
+ my $conf = new FS::Conf;
+ my $expdays = $conf->config('part_pkg-delay_cancel-days') || 1;
+ my $expsecs = 60*60*24*$expdays;
+ return 0 unless $self->expire < time + $expsecs;
+ return 1;
+}
+
=item pkg_label
Returns a label for this package. (Currently "pkgnum: pkg - comment" or
diff --git a/FS/FS/part_pkg/global_Mixin.pm b/FS/FS/part_pkg/global_Mixin.pm
index 263772955..2318c3e61 100644
--- a/FS/FS/part_pkg/global_Mixin.pm
+++ b/FS/FS/part_pkg/global_Mixin.pm
@@ -40,6 +40,10 @@ tie my %a2billing_simultaccess, 'Tie::IxHash', (
'changing packages',
'type' => 'checkbox',
},
+ 'delay_cancel' => {
+ 'name' => 'Automatically suspend for one day before cancelling',
+ 'type' => 'checkbox',
+ },
# miscellany--maybe put this in a separate module?
@@ -109,6 +113,7 @@ tie my %a2billing_simultaccess, 'Tie::IxHash', (
unused_credit_cancel
unused_credit_suspend
unused_credit_change
+ delay_cancel
a2billing_tariff
a2billing_type
diff --git a/FS/FS/reason.pm b/FS/FS/reason.pm
index 864804d26..9c34dd98a 100644
--- a/FS/FS/reason.pm
+++ b/FS/FS/reason.pm
@@ -174,7 +174,7 @@ sub new_or_existing {
}
} else {
my %hash = ('class' => $opt{'class'}, 'type' => $opt{'type'});
- my $reason_type = qsearchs('reason_type', \%hash)
+ $reason_type = qsearchs('reason_type', \%hash)
|| FS::reason_type->new(\%hash);
$error = $reason_type->insert unless $reason_type->typenum;
diff --git a/bin/xmlrpc-customer_bill_now b/bin/xmlrpc-customer_bill_now
new file mode 100755
index 000000000..244b66f06
--- /dev/null
+++ b/bin/xmlrpc-customer_bill_now
@@ -0,0 +1,21 @@
+#!/usr/bin/perl
+
+use strict;
+use Frontier::Client;
+use Data::Dumper;
+
+my $uri = new URI 'http://localhost:8008/';
+
+my $server = new Frontier::Client ( 'url' => $uri );
+
+my $result = $server->call(
+ 'FS.API.bill_now',
+ 'secret' => 'sharingiscaring',
+ 'custnum' => 3,
+);
+
+#die $result->{'error'} if $result->{'error'};
+
+print Dumper($result);
+
+1;
diff --git a/httemplate/elements/change_history_common.html b/httemplate/elements/change_history_common.html
index 9e32bef33..9fc85aa53 100644
--- a/httemplate/elements/change_history_common.html
+++ b/httemplate/elements/change_history_common.html
@@ -106,6 +106,9 @@
$item->fields
)
%>
+% if ( $single_cust && $h_table_descripsub{$item->table} ) {
+ <% &{ $h_table_descripsub{$item->table} }( $item ) %>
+% }
</TD>
</TR>
@@ -172,18 +175,6 @@ my $svc_labelsub = sub {
$label. ': <b>'. encode_entities($item->label($item->history_date)). '</b>';
};
-my $discounts = {};
-my $discount_labelsub = sub {
- my($item, $label) = @_;
- my $dnum = $item->discountnum;
- $discounts->{$dnum} ||= qsearchs({
- 'table'=>'discount',
- 'hashref'=>{'discountnum'=>$dnum}
- });
- my $d = $discounts->{$dnum};
- $label . ': <b>' . encode_entities($d->description_short) . '<b>';
-};
-
my %h_table_labelsub = (
'h_cust_pkg' => $pkg_labelsub,
'h_svc_acct' => $svc_labelsub,
@@ -195,7 +186,24 @@ my %h_table_labelsub = (
'h_svc_external' => $svc_labelsub,
'h_svc_phone' => $svc_labelsub,
#'h_phone_device'
- 'h_cust_pkg_discount' => $discount_labelsub,
+);
+
+my $discounts = {};
+my $discount_descripsub = sub {
+ my($item) = @_;
+ $pkgpart{$item->pkgpart} ||= $item->part_pkg->pkg;
+ my $dnum = $item->discountnum;
+ $discounts->{$dnum} ||= qsearchs({
+ 'table'=>'discount',
+ 'hashref'=>{'discountnum'=>$dnum}
+ });
+ my $d = $discounts->{$dnum};
+ '(<b>' . encode_entities($d->description_short) . '</b>'
+ . ' on <b>' . encode_entities($pkgpart{$item->pkgpart}) . '</b>)';
+};
+
+my %h_table_descripsub = (
+ 'h_cust_pkg_discount' => $discount_descripsub,
);
my $cust_pkg_date_format = '%b %o, %Y';
diff --git a/httemplate/misc/cust_main-cancel.cgi b/httemplate/misc/cust_main-cancel.cgi
index a78a8b3dc..f6fd1e915 100755
--- a/httemplate/misc/cust_main-cancel.cgi
+++ b/httemplate/misc/cust_main-cancel.cgi
@@ -54,10 +54,11 @@ if ( $error ) {
}
else {
warn "cancelling $cust_main";
- $error = $cust_main->cancel(
+ my @error = $cust_main->cancel( #returns list of errors
'ban' => $ban,
'reason' => $reasonnum,
);
+ $error = join(', ',@error);
}
if ( $error ) {
diff --git a/httemplate/view/cust_main/misc.html b/httemplate/view/cust_main/misc.html
index 15def3283..fe0e329c3 100644
--- a/httemplate/view/cust_main/misc.html
+++ b/httemplate/view/cust_main/misc.html
@@ -7,7 +7,7 @@
<TR>
<TD ALIGN="right"><% mt('Status') |h %></TD>
- <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_main->statuscolor %>"><B><% $cust_main->status_label %></B></FONT></TD>
+ <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_main->statuscolor %>"><B><% $status_label %></B></FONT></TD>
</TR>
% my @part_tag = $cust_main->part_tag;
@@ -204,4 +204,9 @@ my $curuser = $FS::CurrentUser::CurrentUser;
my @agentnums = $curuser->agentnums;
+my $status_label = $cust_main->status_label;
+if ($cust_main->is_status_delay_cancel) {
+ $status_label .= ' (Cancelled)';
+}
+
</%init>