IPifony charge download script, stage 1, #18333
[freeside.git] / FS / bin / freeside-ipifony-download
1 #!/usr/bin/perl
2
3 use strict;
4 use Getopt::Std;
5 use Date::Format qw(time2str);
6 use File::Temp qw(tempdir);
7 use Net::SFTP::Foreign;
8 use FS::UID qw(adminsuidsetup);
9 use FS::Record qw(qsearch qsearchs);
10 use FS::cust_main;
11 use FS::Conf;
12 use Text::CSV;
13
14 my %opt;
15 getopts('va:', \%opt);
16
17 #$Net::SFTP::Foreign::debug = -1;
18 sub HELP_MESSAGE { '
19   Usage:
20       freeside-ipifony-download 
21         [ -v ]
22         [ -a archivedir ]
23         freesideuser sftpuser@hostname[:path]
24 ' }
25
26 my @fields = (
27   'custnum',
28   'date_desc',
29   'quantity',
30   'amount',
31   'classname',
32 );
33
34 my $user = shift or die &HELP_MESSAGE;
35 adminsuidsetup $user;
36
37 # for statistics
38 my $num_charges = 0;
39 my $num_errors = 0;
40 my $sum_charges = 0;
41 # cache classnums
42 my %classnum_of;
43
44 if ( $opt{a} ) {
45   die "no such directory: $opt{a}\n"
46     unless -d $opt{a};
47   die "archive directory $opt{a} is not writable by the freeside user\n"
48     unless -w $opt{a};
49 }
50
51 #my $tmpdir = File::Temp->newdir();
52 my $tmpdir = tempdir( CLEANUP => 1 ); #DIR=>somewhere?
53
54 my $host = shift
55   or die &HELP_MESSAGE;
56 my ($sftpuser, $path);
57 $host =~ s/^(.+)\@//;
58 $sftpuser = $1 || $ENV{USER};
59 $host =~ s/:(.*)//;
60 $path = $1;
61
62 # for now assume SFTP download as the only method
63 print STDERR "Connecting to $sftpuser\@$host...\n" if $opt{v};
64
65 my $sftp = Net::SFTP::Foreign->new(
66   host      => $host,
67   user      => $sftpuser,
68   # for now we don't support passwords. use authorized_keys.
69   timeout   => 30,
70   more      => ($opt{v} ? '-v' : ''),
71 );
72 die "failed to connect to '$sftpuser\@$host'\n(".$sftp->error.")\n"
73   if $sftp->error;
74
75 $sftp->setcwd($path) if $path;
76
77 my $files = $sftp->ls('.', wanted => qr/\.csv$/, names_only => 1);
78 if (!@$files) {
79   print STDERR "No charge files found.\n" if $opt{v};
80   exit(-1);
81 }
82 FILE: foreach my $filename (@$files) {
83   print STDERR "Retrieving $filename\n" if $opt{v};
84   $sftp->get("$filename", "$tmpdir/$filename");
85   if($sftp->error) {
86     warn "failed to download $filename\n";
87     next FILE;
88   }
89
90   #move to server archive dir
91   $sftp->rename("$filename", "Archive/$filename");
92   if($sftp->error) {
93     warn "failed to archive $filename on server\n";
94   } # process it anyway though
95
96   #copy to local archive dir
97   if ( $opt{a} ) {
98     print STDERR "Copying $tmpdir/$filename to archive dir $opt{a}\n"
99       if $opt{v};
100     copy("$tmpdir/$filename", $opt{a});
101     warn "failed to copy $tmpdir/$filename to $opt{a}: $!" if $!;
102   }
103
104   open my $fh, "<$tmpdir/$filename";
105   my $header = <$fh>;
106   if ($header !~ /^cust_id/) {
107     warn "warning: $filename has incorrect header row:\n$header\n";
108     # but try anyway
109   }
110   my $csv = Text::CSV->new; # orthodox CSV
111   my %hash;
112   while (my $line = <$fh>) {
113     $csv->parse($line) or do {
114       warn "can't parse $filename: ".$csv->error_input."\n";
115       next FILE;
116     };
117     @hash{@fields} = $csv->fields();
118     my $cust_main = FS::cust_main->by_key($hash{custnum});
119     if (!$cust_main) {
120       warn "customer #$hash{custnum} not found\n";
121       next;
122     }
123     print STDERR "Found customer #$hash{custnum}: ".$cust_main->name."\n"
124       if $opt{v};
125
126     # construct arguments for $cust_main->charge
127     my %opt = (
128       amount      => $hash{amount},
129       quantity    => $hash{quantity},
130       start_date  => $cust_main->next_bill_date,
131       pkg         => $hash{date_desc},
132     );
133     if (my $classname = $hash{classname}) {
134       if (!exists($classnum_of{$classname}) ) {
135         # then look it up
136         my $pkg_class = qsearch('pkg_class', { classname => $classname });
137         $classnum_of{$classname} = $pkg_class ? $pkg_class->classnum : '';
138       }
139       $opt{classnum} = $classnum_of{$classname};
140     }
141     # XXX what's the tax status of these charges?
142     print STDERR "  Charging $hash{amount}\n"
143       if $opt{v};
144     my $error = $cust_main->charge(\%opt);
145     if ($error) {
146       warn "Error creating charge: $error" if $error;
147       $num_errors++;
148     } else {
149       $num_charges++;
150       $sum_charges += $hash{amount};
151     }
152   } #while $line
153   close $fh;
154 } #FILE
155
156 if ($opt{v}) {
157   print STDERR "
158 Finished!
159   Processed files: @$files
160   Created charges: $num_charges
161   Sum of charges: \$".sprintf('%0.2f', $sum_charges)."
162   Errors: $num_errors
163 ";
164 }
165
166 =head1 NAME
167
168 freeside-eftca-download - Retrieve payment batch responses from EFT Canada.
169
170 =head1 SYNOPSIS
171
172   freeside-eftca-download [ -v ] [ -a archivedir ] user
173
174 =head1 DESCRIPTION
175
176 Command line tool to download returned payment reports from the EFT Canada 
177 gateway and void the returned payments.  Uses the login and password from 
178 'batchconfig-eft_canada'.
179
180 -v: Be verbose.
181
182 -a directory: Archive response files in the provided directory.
183
184 user: freeside username
185
186 =head1 BUGS
187
188 You need to manually SFTP to ftp.eftcanada.com from the freeside account 
189 and accept their key before running this script.
190
191 =head1 SEE ALSO
192
193 L<FS::pay_batch>
194
195 =cut
196
197 1;
198