summaryrefslogtreecommitdiff
path: root/FS/FS
diff options
context:
space:
mode:
authorJonathan Prykop <jonathan@freeside.biz>2015-02-16 13:53:20 -0600
committerJonathan Prykop <jonathan@freeside.biz>2015-02-16 13:53:20 -0600
commit251d07aa41b6830a0a2f2a51c14fa94586d843c2 (patch)
treec53f5a96bc595b914187a4c2be0c9a3084bedb98 /FS/FS
parent4771a2fe6202aa77d8e6fda10dc2b221899f3941 (diff)
RT#27710: Credit voiding
Diffstat (limited to 'FS/FS')
-rw-r--r--FS/FS/AccessRight.pm3
-rw-r--r--FS/FS/Schema.pm5
-rw-r--r--FS/FS/access_right.pm3
-rw-r--r--FS/FS/cust_credit.pm13
-rw-r--r--FS/FS/cust_credit_void.pm81
-rw-r--r--FS/FS/reason_type.pm4
6 files changed, 103 insertions, 6 deletions
diff --git a/FS/FS/AccessRight.pm b/FS/FS/AccessRight.pm
index 121f83c..f395dea 100644
--- a/FS/FS/AccessRight.pm
+++ b/FS/FS/AccessRight.pm
@@ -221,6 +221,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',
@@ -233,6 +235,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',
'Add on-the-fly refund reason', #NEW
],
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 54a4680..5333b1a 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1423,6 +1423,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',
@@ -1458,6 +1459,10 @@ sub tables_hashref {
table => 'cust_pkg',
references => [ 'pkgnum' ],
},
+ { columns => [ 'void_reasonnum' ],
+ table => 'reason',
+ references => [ 'reasonnum' ],
+ },
{ columns => [ 'void_usernum' ],
table => 'access_user',
references => [ 'usernum' ],
diff --git a/FS/FS/access_right.pm b/FS/FS/access_right.pm
index d5e3b8b..1ea6e49 100644
--- a/FS/FS/access_right.pm
+++ b/FS/FS/access_right.pm
@@ -251,6 +251,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 deebe27..76fdecb 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -382,13 +382,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';
@@ -403,7 +408,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 f76f794..9c92068 100644
--- a/FS/FS/cust_credit_void.pm
+++ b/FS/FS/cust_credit_void.pm
@@ -1,11 +1,12 @@
package FS::cust_credit_void;
-use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::Record );
+use base qw( FS::otaker_Mixin FS::cust_main_Mixin FS::reason_Mixin FS::Record );
use strict;
use FS::Record qw(qsearchs); # qsearch qsearchs);
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>).
@@ -111,6 +156,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 00ac9a8..17a7167 100644
--- a/FS/FS/reason_type.pm
+++ b/FS/FS/reason_type.pm
@@ -11,6 +11,7 @@ our %class_name = (
'R' => 'credit',
'S' => 'suspend',
'F' => 'refund',
+ 'X' => 'void credit',
);
our %class_purpose = (
@@ -18,6 +19,7 @@ our %class_purpose = (
'R' => 'explain why a customer was credited',
'S' => 'explain why a customer package was suspended',
'F' => 'explain why a customer was refunded',
+ 'X' => 'explain why a credit was voided',
);
=head1 NAME
@@ -48,7 +50,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', 'F' or 'X' for cancel, credit, suspend, refund or void credit
=item type - name of the type of reason