#!/usr/bin/perl

use strict;
use warnings;
use vars qw( %opt );
use Date::Format;
use File::Slurp;
use FS::Misc::Getopt;
use FS::Record qw(qsearch qsearchs dbh);

getopts('cpiXr:t:u:vk:f');

my $dbh = dbh;
$FS::UID::AutoCommit = 0;

sub usage() {
  "Usage: bulk_void  -s start -e end
                  -r void_reason
                  { -c | -p | -i }
                  [ -t payby ]
                  [ -u filename ]
                  [ -k pkgpart ]
                  [ -v ]
                  [ -X ]
                  <user>
-s, -e: date range (required)
-r: void reason text (required)
-c, -p, -i: void credits, payments, invoices
-u: specifies a filename of customer numbers - only void for those customers
-k: skip invoices with only this pkgpart
-t: only void payments with this payby
-f: force - continue voiding invoices even if some have errors
-v: verbose - show more detail
-X: commit changes
";
}

if (!$opt{start} or !$opt{end} or !$opt{r}) {
  die usage;
}

print "DRY RUN--changes will not be committed.\n" unless $opt{X};

my %search = ();
$search{payby} = $opt{t} if $opt{t} && $opt{p};

my $extra_sql = (keys %search ? ' AND ' : ' WHERE ').
                " _date >= $opt{start} AND _date <= $opt{end}";

if ( $opt{u} ) {
  my @custnums = map { chomp; $_; } read_file($opt{u});
  $extra_sql .= ' AND custnum IN ('. join(',', @custnums). ') ';
}

my %tables = (
  c => 'cust_credit',
  p => 'cust_pay',
  i => 'cust_bill',
);

my $reason = $opt{r};


foreach my $k (keys %tables) {
  next unless $opt{$k};
  my $table = $tables{$k};
  debug("$table:");
  my $done_count = 0;
  my $error_count = 0;
  my $pkey = '';

  my $cursor = FS::Cursor->new({
    table     => $table,
    hashref   => \%search,
    extra_sql => $extra_sql,
  });
  my $error;
  while (my $record = $cursor->fetch) {

    if ( $opt{k} && $opt{i} ) {
      my @other_pkgs = grep { $_->pkgpart != $opt{k} }
                         grep $_, map $_->cust_pkg, $record->cust_bill_pkg;
      next if ! @other_pkgs;
    }

    if ( $opt{v} ) {
      $pkey ||= $record->primary_key;
      my $num = $record->get($pkey);
      my $date = time2str('%x', $record->_date);
      my $name = $record->cust_main->name;
      warn "Voiding $table #$num ($date) for $name\n";
    }

    $error = $record->void($reason);
    if ( $error ) {
      $error = "$table #" . $record->get($record->primary_key) . ": $error";
      print "$error\n";
      $error_count++;
      if ( $opt{X} && ! $opt{f} ) {
        $dbh->rollback;
        exit(1);
      }
    } else {
      $done_count++;
    }
  }
  print " $table voided: $done_count\n errors: $error_count\n";
}

if ( $opt{X} ) {
  $dbh->commit;
  print "Committed changes.\n";
} else {
  $dbh->rollback;
  print "Reverted changes.\n";
}

