1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
#!/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:v');
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
-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} ) {
$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";
}
|