Option to ignore old CDRs, RT#81480
[freeside.git] / bin / cust_bill-credit_ship
1 #!/usr/bin/perl -w
2
3 use strict;
4 use vars qw( $opt_d $opt_f $opt_s $opt_p $opt_m $opt_c $opt_e $opt_r );
5 use Getopt::Std;
6 use Date::Parse qw(str2time);
7 use FS::UID qw( dbh adminsuidsetup );
8 use FS::Record qw( qsearch qsearchs );
9 use FS::cust_main_county;
10 use FS::cust_bill;
11 use FS::msg_template;
12 use FS::cust_credit;
13 use FS::cust_credit_bill;
14
15 getopts('d:f:s:pm:c:er');
16
17 my $user = shift or &usage;
18 adminsuidsetup $user;
19
20 #i should be an option
21 my $taxname = 'HST, ON';
22 my $rate = '13';
23                                                                                 
24 my $oldAutoCommit = $FS::UID::AutoCommit;
25 local $FS::UID::AutoCommit = 0;
26 my $dbh = dbh;
27
28 ###
29 # find tax classes with double taxes
30 ##
31
32 #my $sql = "select distinct taxclass from cust_main_county where taxname = '$taxname';";
33 #my $sth = dbh->prepare($sql) or die dbh->errstr;
34 #$sth->execute or die $sth->errstr;
35 #my @taxclasses = map $_->[0], @{ $sth->fetchall_arrayref() };
36 #
37 #my %taxclass = map {
38 #                my $taxclass = $_;
39 #                my @cust_main_county = qsearch('cust_main_county', {
40 #                                         'taxclass' => $taxclass,
41 #                                         'taxname'  => $taxname,
42 #                                         'tax'      => { op=>'>', value=>'0' },
43 #                                       });
44 #
45 #                $taxclass => ( scalar(@cust_main_county) > 1 );
46 #
47 #              } @taxclasses;
48
49 my %taxclass = map { $_ => 1 } (
50   'InternetService',
51   'ComputerSetup-Remote',
52   'Config File- Static IPs',
53   'GST',
54   'GST + PST',
55   'GST ONLY',
56   'HST ONLY',
57   'HardwareRental',
58   'HardwareSale',
59   'HardwareSale_WEx',
60   'InternetService_WEx',
61   'LondDistanceFixedRate',
62   'LondDistanceVariableRate',
63   'PST',
64   'SARA-ONLY-GSTPayable',
65   'SetupInternet',
66   'SetupVoice',
67   'Shipping',
68   'SoftwareLicenseToUse',
69   'TDMPRI',
70   'TDMPRI_WEx',
71   'WarrantyCoverageHardware',
72   'WarrantyCoverageSoftware',
73   'WebHosting',
74   'federal and qst',
75   'telephoneNumber',
76   'LongDistanceFixedRate_WEx',
77   'LongDistanceVariableRate_WEx',
78 );
79
80 my %h;
81 $FS::Record::nowarn_classload = 1;
82 $FS::Record::nowarn_classload = 1;
83
84 ###
85 # find invoices
86 ###
87
88 #my $extra_sql = $opt_s ? " AND cust_main.state = '$opt_s' " : '';
89
90 my $start_date = $opt_d ? str2time($opt_d) : 0;
91 my $end_date = $opt_f ? str2time($opt_f) : 1375471643;
92
93 my @cust_bill = qsearch({
94   'select'    => 'cust_bill.*',
95   'table'     => 'cust_bill',
96   'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
97   'hashref'   => { '_date' => { op=>'>', value=>$start_date } },
98   'extra_sql' => " AND _date < $end_date ",
99 #  'extra_sql' => $extra_sql,
100 });
101
102 my( @billpkgnums, @setuprecurs, @amounts );
103
104 foreach my $cust_bill ( @cust_bill ) {
105
106   my $tax = $cust_bill->tax;
107
108   my $cust_main = $cust_bill->cust_main;
109
110   next if $cust_bill->charged == 0
111        or $tax == 0
112        or $cust_main->tax eq 'Y';
113
114   my $credit = 0;
115
116   @billpkgnums = ();
117   @setuprecurs = ();
118   @amounts = ();
119
120   foreach my $cust_bill_pkg ( $cust_bill->cust_bill_pkg ) {
121     my $cust_pkg = $cust_bill_pkg->cust_pkg or next;
122
123     #my $loc = $cust_pkg->cust_location_or_main;
124     #next if $opt_s && $loc->state ne $opt_s;
125     next if $cust_pkg->cust_location; #we did these already
126     next if $opt_s && $cust_main->ship_state ne $opt_s;
127     next if $opt_s && $cust_main->state eq $opt_s; #we did these already too
128
129     my $part_pkg = $cust_pkg->part_pkg;
130
131     #these were changed already
132     # next unless $taxclass{ $part_pkg->taxclass };
133
134     my $h = $h{$part_pkg->pkgpart} ||= qsearchs({
135       'table'     => 'h_part_pkg', 
136       'hashref'   => { 'pkgpart' => $part_pkg->pkgpart },
137       'extra_sql' => "AND history_action IN ( 'insert', 'replace_new' )",
138       'order_by'  => 'ORDER BY HISTORYNUM DESC LIMIT 1',
139     });
140     next unless $taxclass{ $h->taxclass };
141
142     #my $amount = $cust_bill_pkg->setup + $cust_bill_pkg->recur;
143     #$credit += $rate * $amount / 100;
144
145     if ( $cust_bill_pkg->setup ) {
146       my $scredit = sprintf('%.2f', ($rate * $cust_bill_pkg->setup / 100) + 0.00000001);
147       $credit += $scredit;
148       push @setuprecurs, 'setup';
149       push @billpkgnums, $cust_bill_pkg->billpkgnum;
150       push @amounts, $scredit;
151     } 
152
153     if ( $cust_bill_pkg->recur ) {
154       my $rcredit = sprintf('%.2f', ($rate * $cust_bill_pkg->recur / 100) + 0.00000001);
155       $credit += $rcredit;
156       push @setuprecurs, 'recur';
157       push @billpkgnums, $cust_bill_pkg->billpkgnum;
158       push @amounts, $rcredit;
159     }
160
161   }
162
163   $credit = sprintf('%.2f', $credit + 0.00000001);
164
165   next if $credit == 0;
166
167   #$credit = sprintf('%.2f', $credit + 0.00000001);
168
169   warn "invoice ". $cust_bill->invnum. ": credit of $credit is more than orignally taxed ($tax)\n" if $credit > $tax;
170
171   warn "invoice ". $cust_bill->invnum. ": credit of $credit is more than 50% of originally taxed ($tax)\n" if $credit-0.01 > $tax/2;
172
173   #my $cr_percent = sprintf('%.1f', 100 * $credit / $tax);
174
175   if ( $opt_p ) {
176     #print $cust_bill->invnum. ','. $cust_bill->custnum. ",$tax,$credit,$cr_percent%\n";
177     print $cust_bill->invnum. ','. $cust_bill->custnum. ',"'.
178           $cust_bill->cust_main->name. '",'. "$tax,$credit\n";
179   }
180
181 #COMMENTING OUT ALL DANGEROUT STUFF
182 #
183 #  if ( $opt_m && ! $opt_r ) {
184 #
185 #    my $msg_template = qsearchs('msg_template', { 'msgnum' => $opt_m } )
186 #        or die "Template $opt_m not found";
187 #    my $error = $msg_template->send(
188 #      'cust_main' => $cust_main,
189 #      'object'    => $cust_main,
190 #    );
191 #    if ( $error ) {
192 #      warn "error sending email for invnum ". $cust_bill->invnum. ','.
193 #           " custnum ". $cust_bill->custnum. ": $error\n";
194 #    }
195 #  }
196 #
197 #  if ( $opt_c ) {
198 #    my $cust_credit = new FS::cust_credit {
199 #      'custnum'   => $cust_main->custnum,
200 #      'amount'    => $credit,
201 #      'reasonnum' => $opt_c,
202 #    };
203 #    my $error = $cust_credit->insert;
204 #    if ( $error ) {
205 #      warn "error inserting credit: $error\n";
206 #    }
207 #    my $cust_credit_bill = new FS::cust_credit_bill {
208 #      'crednum' => $cust_credit->crednum,
209 #      'invnum'  => $cust_bill->invnum,
210 #      'amount'  => $credit,
211 #    };
212 #    my $aerror = $cust_credit_bill->insert;
213 #    if ( $aerror ) {
214 #      warn "error applying credit to invnum ". $cust_bill->invnum. ": $aerror\n";
215 #    }
216 #  }
217 #
218 #  if ( $opt_e && ! $opt_r ) {
219 #    eval { $cust_bill->email };
220 #    if ( $@ ) {
221 #      warn "error sending invnum ". $cust_bill->invnum. ','.
222 #         " custnum ". $cust_bill->custnum. ": $@\n";
223 #    }
224 #  }
225
226 }
227
228 if ( $opt_r ) {
229   $dbh->rollback or die $dbh->errstr; #if $oldAutoCommit;
230 } else {
231   $dbh->commit or die $dbh->errstr; #if $oldAutoCommit;
232 }
233
234 sub usage {                                                                     
235   die "usage:  cust_bill-credit [ -d date ] [ -s state ] [ -p ] [ -m templatenum ] [ -c reasonnum ] [ -e ] [ -r ] employee_username\n";
236 }                                                                               
237                                                                                 
238 =head1 NAME                                                                     
239                                                                                 
240 cust_bill-credit
241                                                                                 
242 =head1 SYNOPSIS                                                                 
243                                                                                 
244   cust_bill-credit [ -d date ] [ -s state ] [ -p ] [ -m templatenum ] [ -c reasonnum ] [ -e ] employee_username   
245                                                                                 
246 =head1 DESCRIPTION
247
248 Command-line tool to search for and credit invoices.
249
250 -d: Search for invoices starting from this date
251
252 -f: Search for invoice finishing on this date
253
254 -s: Search for invoices for packages within this state
255
256 -p: Print an informational line for each invoice with invnum, custnum, original tax amount, calculate credit, and credit percentage of original.
257
258 -m: Send an email to the customer with this message template.
259
260 -c: Credit the invoice for one-half of the taxation amount, using this reason.
261
262 -e: re-Email invoice
263
264 -r: dRy run
265
266 employee_username
267
268 =head1 BUGS
269
270 =head1 SEE ALSO
271
272 L<FS::part_pkg>
273
274 =cut
275
276 1;