summaryrefslogtreecommitdiff
path: root/FS
diff options
context:
space:
mode:
Diffstat (limited to 'FS')
-rw-r--r--FS/FS/AccessRight.pm3
-rw-r--r--FS/FS/Schema.pm1
-rw-r--r--FS/FS/access_right.pm3
-rw-r--r--FS/FS/cust_credit.pm13
-rw-r--r--FS/FS/cust_credit_void.pm79
-rw-r--r--FS/FS/reason_type.pm4
6 files changed, 98 insertions, 5 deletions
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index 24942419d..e97cc54cd 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -222,6 +222,8 @@ tie my %rights, 'Tie::IxHash',
{ rightname=>'Backdate credit', desc=>'Enable credits to be posted for days other than today.' },
'Credit line items', #NEWNEWNEW
'Apply credit', #NEWNEW
+ 'Void credit', #NEWER than things marked NEWNEWNEW
+ 'Unvoid credit', #NEWER than things marked NEWNEWNEW
{ rightname=>'Unapply credit', desc=>'Enable "unapplication" of unclosed credits.' }, #aka unapplycredits
{ rightname=>'Delete credit', desc=>'Enable deletion of unclosed credits. Be very careful! Only delete credits that were data-entry errors, not adjustments.' }, #aka. deletecredits Optionally specify one or more comma-separated email addresses to be notified when a credit is deleted.
'View refunds',
@@ -234,6 +236,7 @@ tie my %rights, 'Tie::IxHash',
'Refund Echeck payment',
'Delete refund', #NEW
'Add on-the-fly credit reason', #NEW
+ 'Add on-the-fly void credit reason',
],
###
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 8087304d7..23bd81d6b 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1036,6 +1036,7 @@ sub tables_hashref {
#void fields
'void_date', @date_type, '', '',
'void_reason', 'varchar', 'NULL', $char_d, '', '',
+ 'void_reasonnum', 'int', 'NULL', '', '', '',
'void_usernum', 'int', 'NULL', '', '', '',
],
'primary_key' => 'crednum',
diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm
index ea3bd4298..b44c21c33 100644
--- a/FS/FS/access_right.pm
+++ b/FS/FS/access_right.pm
@@ -248,6 +248,9 @@ sub _upgrade_data { # class method
'List customers' => 'List contacts',
'Backdate payment' => 'Backdate credit',
'Generate quotation' => 'Disable quotation',
+ 'Void credit' => 'Void credit',
+ 'Unvoid credit' => 'Unvoid credit',
+ 'Add on-the-fly void credit reason' => 'Add on-the-fly void credit reason',
);
# foreach my $old_acl ( keys %onetime ) {
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index badb67351..752d84275 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -348,13 +348,18 @@ adds a record of the voided credit to the cust_credit_void table.
=cut
-# yes, false laziness with cust_pay and cust_bill
-# but frankly I don't have time to fix it now
-
sub void {
my $self = shift;
my $reason = shift;
+ unless (ref($reason) || !$reason) {
+ $reason = FS::reason->new_or_existing(
+ 'class' => 'X',
+ 'type' => 'Void credit',
+ 'reason' => $reason
+ );
+ }
+
local $SIG{HUP} = 'IGNORE';
local $SIG{INT} = 'IGNORE';
local $SIG{QUIT} = 'IGNORE';
@@ -369,7 +374,7 @@ sub void {
my $cust_credit_void = new FS::cust_credit_void ( {
map { $_ => $self->get($_) } $self->fields
} );
- $cust_credit_void->set('void_reason', $reason);
+ $cust_credit_void->set('void_reasonnum', $reason->reasonnum);
my $error = $cust_credit_void->insert;
if ( $error ) {
$dbh->rollback if $oldAutoCommit;
diff --git a/FS/FS/cust_credit_void.pm b/FS/FS/cust_credit_void.pm
index ac47d954a..8d06a72f1 100644
--- a/FS/FS/cust_credit_void.pm
+++ b/FS/FS/cust_credit_void.pm
@@ -6,6 +6,7 @@ use FS::Record qw(qsearch qsearchs dbh fields);
use FS::CurrentUser;
use FS::access_user;
use FS::cust_credit;
+use FS::UID qw( dbh );
=head1 NAME
@@ -85,6 +86,7 @@ sub check {
|| $self->ut_numbern('void_date')
|| $self->ut_textn('void_reason')
|| $self->ut_foreign_keyn('void_usernum', 'access_user', 'usernum')
+ || $self->ut_foreign_keyn('void_reasonnum', 'reason', 'reasonnum')
;
return $error if $error;
@@ -96,6 +98,49 @@ sub check {
$self->SUPER::check;
}
+=item unvoid
+
+"Un-void"s this credit: Deletes the voided credit from the database and adds
+back (but does not re-apply) a normal credit.
+
+=cut
+
+sub unvoid {
+ my $self = shift;
+
+ 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;
+
+ my $cust_credit = new FS::cust_credit ( {
+ map { $_ => $self->get($_) } grep { $_ !~ /void/ } $self->fields
+ } );
+ my $error = $cust_credit->insert;
+
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $error ||= $self->delete;
+ if ( $error ) {
+ $dbh->rollback if $oldAutoCommit;
+ return $error;
+ }
+
+ $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+ '';
+
+}
+
=item cust_main
Returns the parent customer object (see L<FS::cust_main>).
@@ -118,6 +163,40 @@ sub void_access_user {
qsearchs('access_user', { 'usernum' => $self->void_usernum } );
}
+=item void_access_user_name
+
+Returns the voiding employee name.
+
+=cut
+
+sub void_access_user_name {
+ my $self = shift;
+ my $user = $self->void_access_user;
+ return unless $user;
+ return $user->name;
+}
+
+=item void_reason
+
+Returns the text of the associated void credit reason (see L<FS::reason>) for this voided credit.
+
+The reason for the original credit remains accessible through the reason method.
+
+=cut
+
+sub void_reason {
+ my ($self, $value, %options) = @_;
+ my $reason_text;
+ if ( $self->void_reasonnum ) {
+ my $reason = FS::reason->by_key($self->void_reasonnum);
+ $reason_text = $reason->reason;
+ } else { # in case one of these somehow still exists
+ $reason_text = $self->get('void_reason');
+ }
+
+ return $reason_text;
+}
+
=back
=head1 BUGS
diff --git a/FS/FS/reason_type.pm b/FS/FS/reason_type.pm
index a603809e2..4a8c03632 100644
--- a/FS/FS/reason_type.pm
+++ b/FS/FS/reason_type.pm
@@ -10,12 +10,14 @@ our %class_name = (
'C' => 'cancel',
'R' => 'credit',
'S' => 'suspend',
+ 'X' => 'void credit',
);
our %class_purpose = (
'C' => 'explain why a customer package was cancelled',
'R' => 'explain why a customer was credited',
'S' => 'explain why a customer package was suspended',
+ 'X' => 'explain why a credit was voided',
);
=head1 NAME
@@ -46,7 +48,7 @@ inherits from FS::Record. The following fields are currently supported:
=item typenum - primary key
-=item class - currently 'C', 'R', or 'S' for cancel, credit, or suspend
+=item class - currently 'C', 'R', 'S' or 'X' for cancel, credit, suspend, refund or void credit
=item type - name of the type of reason