delete invoices, RT#4048
authorivan <ivan>
Sun, 4 Oct 2009 02:09:14 +0000 (02:09 +0000)
committerivan <ivan>
Sun, 4 Oct 2009 02:09:14 +0000 (02:09 +0000)
FS/FS/AccessRight.pm
FS/FS/Conf.pm
FS/FS/Setup.pm
FS/FS/cust_bill.pm
FS/FS/cust_bill_pkg.pm
FS/bin/freeside-addgroup
bin/mapsecrets2access_user
httemplate/misc/delete-cust_bill.html [new file with mode: 0644]
httemplate/view/cust_bill.cgi
httemplate/view/cust_main/payment_history.html
httemplate/view/cust_main/payment_history/invoice.html

index fc309fa..a54d270 100644 (file)
@@ -150,6 +150,7 @@ tie my %rights, 'Tie::IxHash',
   'Customer invoice / financial info rights' => [
     'View invoices',
     'Resend invoices', #NEWNEW
+    'Delete invoices', #new, but no need to phase in
     'View customer tax exemptions', #yow
     'Add customer tax adjustment', #new, but no need to phase in
     'View customer batched payments', #NEW
@@ -285,14 +286,38 @@ tie my %rights, 'Tie::IxHash',
   
 =item rights
 
-Returns a list of right names.
+Returns the full list of right names.
 
 =cut
   
-  sub rights {
+sub rights {
   #my $class = shift;
   map { ref($_) ? $_->{'rightname'} : $_ } map @{ $rights{$_} }, keys %rights;
-  }
+}
+
+=item default_superuser_rights
+
+Most (but not all) right names.
+
+=cut
+
+sub default_superuser_rights {
+  my $class = shift;
+  my %omit = map { $_=>1 } (
+    'Delete customer',
+    'Delete invoices',
+    'Delete payment',
+    'Delete credit', #?
+    'Delete refund', #?
+    'Time queue',
+    'Redownload resolved batches',
+    'Raw SQL',
+    'Configuration download',
+  );
+
+  no warnings 'uninitialized';
+  grep { ! $omit{$_} } $class->rights;
+}
   
 =item rights_info
 
index 7112417..3b37fea 100644 (file)
@@ -728,7 +728,14 @@ worry that config_items is freeside-specific and icky.
   {
     'key'         => 'deletecustomers',
     'section'     => 'UI',
-    'description' => 'Enable customer deletions.  Be very careful!  Deleting a customer will remove all traces that this customer ever existed!  It should probably only be used when auditing a legacy database.  Normally, you cancel all of a customers\' packages if they cancel service.',
+    'description' => 'Enable customer deletions.  Be very careful!  Deleting a customer will remove all traces that the customer ever existed!  It should probably only be used when auditing a legacy database.  Normally, you cancel all of a customers\' packages if they cancel service.',
+    'type'        => 'checkbox',
+  },
+
+  {
+    'key'         => 'deleteinvoices',
+    'section'     => 'UI',
+    'description' => 'Enable invoices deletions.  Be very careful!  Deleting an invoice will remove all traces that the invoice ever existed!  Normally, you would apply a credit against the invoice instead.',  #invoice voiding?
     'type'        => 'checkbox',
   },
 
index cba3c7e..edfe912 100644 (file)
@@ -364,7 +364,7 @@ sub populate_access {
   use FS::AccessRight;
   use FS::access_right;
 
-  foreach my $rightname ( FS::AccessRight->rights ) {
+  foreach my $rightname ( FS::AccessRight->default_superuser_rights ) {
     my $access_right = new FS::access_right {
       'righttype'   => 'FS::access_group',
       'rightobjnum' => 1, #$supergroup->groupnum,
index 79f85c8..55faa36 100644 (file)
@@ -160,7 +160,50 @@ Really, don't use it.
 sub delete {
   my $self = shift;
   return "Can't delete closed invoice" if $self->closed =~ /^Y/i;
-  $self->SUPER::delete(@_);
+
+  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;
+
+  foreach my $table (qw(
+    cust_bill_event
+    cust_event
+    cust_credit_bill
+    cust_bill_pay
+    cust_bill_pay
+    cust_credit_bill
+    cust_pay_batch
+    cust_bill_pay_batch
+    cust_bill_pkg
+  )) {
+
+    foreach my $linked ( $self->$table() ) {
+      my $error = $linked->delete;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
+      }
+    }
+
+  }
+
+  my $error = $self->SUPER::delete(@_);
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  '';
+
 }
 
 =item replace OLD_RECORD
@@ -460,6 +503,16 @@ sub cust_pay {
   #;
 }
 
+sub cust_pay_batch {
+  my $self = shift;
+  qsearch('cust_pay_batch', { 'invnum' => $self->invnum } );
+}
+
+sub cust_bill_pay_batch {
+  my $self = shift;
+  qsearch('cust_bill_pay_batch', { 'invnum' => $self->invnum } );
+}
+
 =item cust_bill_pay
 
 Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice.
@@ -474,6 +527,8 @@ sub cust_bill_pay {
 
 =item cust_credited
 
+=item cust_credit_bill
+
 Returns all applied credits (see L<FS::cust_credit_bill>) for this invoice.
 
 =cut
@@ -485,6 +540,10 @@ sub cust_credited {
   ;
 }
 
+sub cust_credit_bill {
+  shift->cust_credited(@_);
+}
+
 =item cust_bill_pay_pkgnum PKGNUM
 
 Returns all payment applications (see L<FS::cust_bill_pay>) for this invoice
index 96f09c9..9d7aae2 100644 (file)
@@ -1,7 +1,7 @@
 package FS::cust_bill_pkg;
 
 use strict;
-use vars qw( @ISA $DEBUG );
+use vars qw( @ISA $DEBUG $me );
 use FS::Record qw( qsearch qsearchs dbdef dbh );
 use FS::cust_main_Mixin;
 use FS::cust_pkg;
@@ -12,10 +12,14 @@ use FS::cust_bill_pkg_display;
 use FS::cust_bill_pay_pkg;
 use FS::cust_credit_bill_pkg;
 use FS::cust_tax_exempt_pkg;
+use FS::cust_bill_pkg_tax_location;
+use FS::cust_bill_pkg_tax_rate_location;
+use FS::cust_tax_adjustment;
 
 @ISA = qw( FS::cust_main_Mixin FS::Record );
 
-$DEBUG = 0;
+$DEBUG = 2;
+$me = '[FS::cust_bill_pkg]';
 
 =head1 NAME
 
@@ -40,30 +44,57 @@ supported:
 
 =over 4
 
-=item billpkgnum - primary key
+=item billpkgnum
 
-=item invnum - invoice (see L<FS::cust_bill>)
+primary key
 
-=item pkgnum - package (see L<FS::cust_pkg>) or 0 for the special virtual sales tax package, or -1 for the virtual line item (itemdesc is used for the line)
+=item invnum
 
-=item pkgpart_override - optional package definition (see L<FS::part_pkg>) override
-=item setup - setup fee
+invoice (see L<FS::cust_bill>)
 
-=item recur - recurring fee
+=item pkgnum
 
-=item sdate - starting date of recurring fee
+package (see L<FS::cust_pkg>) or 0 for the special virtual sales tax package, or -1 for the virtual line item (itemdesc is used for the line)
 
-=item edate - ending date of recurring fee
+=item pkgpart_override
 
-=item itemdesc - Line item description (overrides normal package description)
+optional package definition (see L<FS::part_pkg>) override
 
-=item quantity - If not set, defaults to 1
+=item setup
 
-=item unitsetup - If not set, defaults to setup
+setup fee
 
-=item unitrecur - If not set, defaults to recur
+=item recur
 
-=item hidden - If set to Y, indicates data should not appear as separate line item on invoice
+recurring fee
+
+=item sdate
+
+starting date of recurring fee
+
+=item edate
+
+ending date of recurring fee
+
+=item itemdesc
+
+Line item description (overrides normal package description)
+
+=item quantity
+
+If not set, defaults to 1
+
+=item unitsetup
+
+If not set, defaults to setup
+
+=item unitrecur
+
+If not set, defaults to recur
+
+=item hidden
+
+If set to Y, indicates data should not appear as separate line item on invoice
 
 =back
 
@@ -196,13 +227,65 @@ sub insert {
 
 =item delete
 
-Currently unimplemented.  I don't remove line items because there would then be
-no record the items ever existed (which is bad, no?)
+Not recommended.
 
 =cut
 
 sub delete {
-  return "Can't delete cust_bill_pkg records!";
+  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;
+
+  foreach my $table (qw(
+    cust_bill_pkg_detail
+    cust_bill_pkg_display
+    cust_bill_pkg_tax_location
+    cust_bill_pkg_tax_rate_location
+    cust_tax_exempt_pkg
+    cust_bill_pay_pkg
+    cust_credit_bill_pkg
+  )) {
+
+    foreach my $linked ( qsearch($table, { billpkgnum=>$self->billpkgnum }) ) {
+      my $error = $linked->delete;
+      if ( $error ) {
+        $dbh->rollback if $oldAutoCommit;
+        return $error;
+      }
+    }
+
+  }
+
+  foreach my $cust_tax_adjustment (
+    qsearch('cust_tax_adjustment', { billpkgnum=>$self->billpkgnum })
+  ) {
+    $cust_tax_adjustment->billpkgnum(''); #NULL
+    my $error = $cust_tax_adjustment->replace;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return $error;
+    }
+  }
+
+  my $error = $self->SUPER::delete(@_);
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+
+  '';
+
 }
 
 #alas, bin/follow-tax-rename
@@ -263,6 +346,7 @@ Returns the package (see L<FS::cust_pkg>) for this invoice line item.
 
 sub cust_pkg {
   my $self = shift;
+  warn "$me $self -> cust_pkg";
   qsearchs( 'cust_pkg', { 'pkgnum' => $self->pkgnum } );
 }
 
index 7b30f7d..25c2345 100755 (executable)
@@ -24,7 +24,7 @@ my $error = $access_group->insert;
 die $error if $error;
 
 if ( $opt_s ) {
-  foreach my $rightname ( FS::AccessRight->rights ) {
+  foreach my $rightname ( FS::AccessRight->default_superuser_rights ) {
     my $access_right = new FS::access_right {
       'righttype'   => 'FS::access_group',
       'rightobjnum' => $access_group->groupnum,
index 945f130..d632360 100755 (executable)
@@ -24,7 +24,7 @@ unless ( $supergroup ) {
   my $error = $supergroup->insert;
   die $error if $error;
 
-  foreach my $rightname ( FS::AccessRight->rights ) {
+  foreach my $rightname ( FS::AccessRight->default_superuser_rights ) {
     my $access_right = new FS::access_right {
       'righttype'   => 'FS::access_group',
       'rightobjnum' => $supergroup->groupnum,
diff --git a/httemplate/misc/delete-cust_bill.html b/httemplate/misc/delete-cust_bill.html
new file mode 100644 (file)
index 0000000..3a642b0
--- /dev/null
@@ -0,0 +1,21 @@
+% if ( $error ) {
+%   errorpage($error);
+% } else {
+<% $cgi->redirect($p. "view/cust_main.cgi?". $custnum) %>
+% }
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Delete invoices');
+
+#untaint invnum
+my($query) = $cgi->keywords;
+$query =~ /^(\d+)$/ || die "Illegal crednum";
+my $invnum = $1;
+
+my $cust_bill = qsearchs('cust_bill',{'invnum'=>$invnum});
+my $custnum = $cust_bill->custnum;
+
+my $error = $cust_bill->delete;
+
+</%init>
index 2673e82..5540221 100755 (executable)
@@ -2,10 +2,31 @@
   "View this customer (#$display_custnum)" => "${p}view/cust_main.cgi?$custnum",
 )) %>
 
+% if ( $conf->exists('deleteinvoices')
+%      && $curuser->access_right('Delete invoices' )
+%    )
+% {
+
+    <SCRIPT TYPE="text/javascript">
+    function areyousure(href, message) {
+      if (confirm(message) == true)
+        window.location.href = href;
+    }
+    </SCRIPT>
+
+    <A HREF  = "javascript:areyousure(
+                  '<%$p%>misc/delete-cust_bill.html?<% $invnum %>',
+                  'Are you sure you want to delete this invoice?'
+               )"
+       TITLE = "Delete this invoice from the database completely"
+    >Delete this invoice</A>
+    <BR><BR>
+
+% }
 
 % if ( $cust_bill->owed > 0
 %      && scalar( grep $payby{$_}, qw(BILL CASH WEST MCRD) )
-%      && $FS::CurrentUser::CurrentUser->access_right('Post payment')
+%      && $curuser->access_right('Post payment')
 %      && ! $conf->exists('pkg-balances')
 %    )
 % {
@@ -37,8 +58,7 @@
 
 % } 
 
-
-% if ( $FS::CurrentUser::CurrentUser->access_right('Resend invoices') ) {
+% if ( $curuser->access_right('Resend invoices') ) {
 
     <A HREF="<% $p %>misc/print-invoice.cgi?<% $link %>">Re-print this invoice</A>
 
 
 % } 
 
-
 % if ( $conf->exists('invoice_latex') ) { 
 
-  <A HREF="<% $p %>view/cust_bill-pdf.cgi?<% $link %>.pdf">View typeset invoice</A>
+  <A HREF="<% $p %>view/cust_bill-pdf.cgi?<% $link %>.pdf">View typeset invoice PDF</A>
   <BR><BR>
 % } 
 
 <% include('/elements/footer.html') %>
 <%init>
 
+my $curuser = $FS::CurrentUser::CurrentUser;
+
 die "access denied"
-  unless $FS::CurrentUser::CurrentUser->access_right('View invoices');
+  unless $curuser->access_right('View invoices');
 
 #untaint invnum
 my($query) = $cgi->keywords;
@@ -105,7 +126,7 @@ my $cust_bill = qsearchs({
   'table'     => 'cust_bill',
   'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
   'hashref'   => { 'invnum' => $invnum },
-  'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
+  'extra_sql' => ' AND '. $curuser->agentnums_sql,
 });
 die "Invoice #$invnum not found!" unless $cust_bill;
 
@@ -117,5 +138,3 @@ my $display_custnum = $cust_bill->cust_main->display_custnum;
 my $link = $templatename ? "$templatename-$invnum" : $invnum;
 
 </%init>
-
-
index 0050daf..2ac3f26 100644 (file)
@@ -379,7 +379,7 @@ my %opt = (
         qw( card_refund-days )
   ),
   ( map { $_ => $conf->exists($_) } 
-        qw( deletepayments deleterefunds pkg-balances )
+        qw( deleteinvoices deletepayments deleterefunds pkg-balances )
   )
 );
 
index 39c6739..c0d32df 100644 (file)
@@ -1,9 +1,11 @@
-<% $link %><% $pre %>Invoice #<% $invnum %>
-(Balance $ <% $cust_bill->owed %>)<% $post %><% $link ? '</A>' : '' %><% $events %>
+<% $link %><% $pre %>Invoice #<% $cust_bill->display_invnum %>
+(Balance $ <% $cust_bill->owed %>)<% $post %><% $link ? '</A>' : '' %><% $delete %><% $events %>
 <%init>
 
 my( $cust_bill, %opt ) = @_;
 
+my $conf = new FS::Conf;
+
 my $curuser = $FS::CurrentUser::CurrentUser;
 
 my($pre, $post) = ('', '');
@@ -18,6 +20,15 @@ my $link = $curuser->access_right('View invoices')
              ? qq!<A HREF="${p}view/cust_bill.cgi?$invnum">!
              : '';
 
+my $delete = '';
+if ( $opt{'deleteinvoices'} && $curuser->access_right('Delete invoices') ) {
+  $delete = qq! (<A HREF="javascript:areyousure('!.
+            qq!${p}misc/delete-cust_bill.html?$invnum',!.
+            qq!'Are you sure you want to delete this invoice?')"!.
+            qq! TITLE="Delete this invoice from the database completely"!.
+            qq!>delete</A>)!;
+}
+
 my $events = '';
 #1.9
 if ( $cust_bill->num_cust_event
@@ -26,8 +37,8 @@ if ( $cust_bill->num_cust_event
         )
    ) {
   $events =
-    qq!<BR><FONT SIZE="-1"><A HREF="${p}search/cust_event.html?invnum=!.
-    $cust_bill->invnum. '">(&nbsp;View invoice events&nbsp;)</A></FONT>';
+    qq!<BR><FONT SIZE="-1"><A HREF="${p}search/cust_event.html?invnum=$invnum!.
+    '">(&nbsp;View invoice events&nbsp;)</A></FONT>';
 }
 #