optionally calculate Suretax taxes based only on the service location, #32043
[freeside.git] / FS / FS / pay_batch / nacha.pm
1 package FS::pay_batch::nacha;
2
3 use strict;
4 use vars qw( %import_info %export_info $name $conf $entry_hash $DEBUG );
5 use Date::Format;
6 #use Time::Local 'timelocal';
7 #use FS::Conf;
8
9 $name = 'NACHA';
10 my $i;
11 $DEBUG = 0;
12
13 %import_info = (
14   #XXX stub finish me
15   'filetype' => 'CSV',
16   'fields' => [
17   ],
18   'hook' => sub {
19     my $hash = shift;
20   },
21   'approved' => sub { 1 },
22   'declined' => sub { 0 },
23 );
24
25 %export_info = (
26
27   #optional
28   init => sub {
29     $conf = shift;
30   },
31
32   delimiter => '',
33
34
35   header => sub {
36     my( $pay_batch, $cust_pay_batch_arrayref ) = @_;
37
38     $conf->config('batchconfig-nacha-destination') =~ /^\s*(\d{9})\s*$/
39       or die 'illegal NACHA Destination';
40     my $dest = $1;
41
42     my $dest_name = $conf->config('batchconfig-nacha-destination_name');
43     $dest_name = substr( $dest_name. (' 'x23), 0, 23);
44
45     $conf->config('batchconfig-nacha-origin') =~ /^\s*(\d{10})\s*$/
46       or die 'illegal NACHA Origin';
47     my $origin = $1;
48
49     my $company = $conf->config('company_name', $pay_batch->agentnum);
50
51     my $origin_name =  $conf->config('batchconfig-nacha-origin_name')
52                     || $company;
53     $origin_name = substr(uc($origin_name). (' 'x23), 0, 23);
54
55     $company = substr(uc($company). (' 'x16), 0, 16);
56
57     my $now = time;
58
59     #haha don't want to break after a quarter million years of a batch a day
60     #or 54 years for 5000 agent-virtualized hosted companies batching daily
61     my $refcode = substr( (' 'x8). $pay_batch->batchnum, -8);
62
63     #or only 25,000 years or 5.4 for 5000 companies :)
64     #though they would probably want them numbered per company
65     my $batchnum = substr( ('0'x7). $pay_batch->batchnum, -7);
66
67     $entry_hash = 0;
68
69     warn "building File & Batch Header Records\n" if $DEBUG;
70
71     ##
72     # File Header Record
73     ##
74
75     '1'.                      #Record Type Code
76     '01'.                     #Priority Code
77     ' '. $dest.               #Immediate Destination / 9-digit transit routing #
78     $origin.                  #Immediate Origin / 10 digit company number
79     time2str('%y%m%d', $now). #File Creation Date
80     time2str('%H%M',   $now). #File Creation Time
81     'A'.                 #XXX file ID modifier, mult. files in transit? [A-Z0-9]
82     '094'.                    #94 character records
83     '10'.                     #Blocking Factor
84     '1'.                      #Format code
85     $dest_name.               #Immediate Destination Name / 23 char bank name
86     $origin_name.             #Immediate Origin Name / 23 char company name
87     $refcode. "\n".           #Reference Code (internal/optional)
88
89     ###
90     # Batch Header Record
91     ###
92
93     '5'.                     #Record Type Code
94     '225'.                   #Service Class Code (220 credits only,
95                              #                    200 mixed debits & credits)
96     $company.                #on cust. statements
97     (' 'x20 ).               #20 char "company internal use if desired"
98     $origin.                 #Company Identification (Immediate Origin)
99     'PPD'. #others?
100            #PPD "Prearranged Payments and Deposit entries" for consumer items
101            #CCD (Cash Concentration and Disbursement)
102            #CTX (Corporate Trade Exchange)
103            #TEL (Telephone initiated entires)
104            #WEB (Authorization received via the Internet)
105     'InterntSvc'. #XXX from conf 10 char txn desc, printed on cust. statements
106
107     #6 char "Descriptive date" printed on customer statements
108     #XXX now? or use a separate post date?
109     time2str('%y%m%d', $now).
110
111     #6 char date transactions are to be posted
112     #XXX now? or do we need a future banking day date like eft_canada trainwreck
113     time2str('%y%m%d', $now).
114
115     (' 'x3).                 #Settlement Date / Reserved
116     '1'.                     #Originator Status Code
117     substr($dest, 0, 8).     #Originating Financial Institution
118     $batchnum. "\n"          #Batch Number ("number batches sequentially")
119
120   },
121
122   'row' => sub {
123     my( $cust_pay_batch, $pay_batch, $batchcount, $batchtotal ) = @_;
124
125     my ($account, $aba) = split('@', $cust_pay_batch->payinfo);
126
127     $conf->config('batchconfig-nacha-destination') =~ /^\s*(\d{9})\s*$/
128       or die 'illegal NACHA Destination';
129     my $dest = $1;
130
131     # "Total of all positions 4-11 on each 6 record"
132     $entry_hash += substr($aba,0,8); 
133
134     my $cust_main = $cust_pay_batch->cust_main;
135     my $cust_identifier = substr($cust_main->display_custnum. (' 'x15), 0, 15);
136
137     #XXX paytype should actually be in the batch, but this will do for now
138     #27 checking debit, 37 savings debit
139     my $transaction_code = ( $cust_pay_batch->paytype =~ /savings/i ? '37' : '27' );
140
141     # not $self->payname?
142     my $cust_name = substr($cust_main->name. (' 'x22), 0, 22);
143     $i++;
144     my $tracenum = $dest. substr(('0'x7). $i, -6);
145     #non-PPD transactions?  future
146
147     warn "building PPD Record\n" if $DEBUG;
148
149     ###
150     # PPD Entry Detail Record
151     ###
152
153     '6'.                              #Record Type Code
154     $transaction_code.                #Transaction Code
155     $aba.                             #Receiving DFI Identification, check digit
156     substr($account.(' 'x17), 0, 17). #DFI Account number (Left justify)
157     sprintf('%010.0f', $cust_pay_batch->amount * 100). #Amount
158     $cust_identifier.                 #Individual Identification Number, 15 char
159     $cust_name.                       #Individual name (22-char)
160     '  '.                             #2 char "company internal use if desired"
161     '0'.                              #Addenda Record Indicator
162 #    (' 'x15).                         #15 digit "bank will assign trace number"
163      $tracenum.
164     "\n"                              # (00000?)
165   },
166
167   'footer' => sub {
168     my( $pay_batch, $batchcount, $batchtotal ) = @_;
169
170     #Only use the final 10 positions in the entry
171     $entry_hash = substr( '00'.$entry_hash, -10); 
172
173     $conf->config('batchconfig-nacha-destination') =~ /^\s*(\d{9})\s*$/
174       or die 'illegal NACHA Destination';
175     my $dest = $1;
176
177     $conf->config('batchconfig-nacha-origin') =~ /^\s*(\d{10})\s*$/
178       or die 'illegal NACHA Origin';
179     my $origin = $1;
180
181     my $batchnum = substr( ('0'x7). $pay_batch->batchnum, -7);
182
183     my $lines = $batchcount + 4;
184     my $blocks = int($lines/10);
185     my $fill = '';
186
187     if ( my $remainder = $lines % 10 ) {
188       $blocks++;
189       $fill = ("\n".('9'x94))x( 10 - $remainder );
190     }
191
192     warn "building Batch & File Control Records\n" if $DEBUG;
193
194     ###
195     # Batch Control Record
196     ###
197
198     '8'.                          #Record Type Code
199     '225'.                        #Service Class Code (220 credits only,
200                                   #                    200 mixed debits&credits)
201     sprintf('%06d', $batchcount). #Entry / Addenda Count
202     $entry_hash.
203     sprintf('%012.0f', $batchtotal * 100). #Debit total
204     '000000000000'.               #Credit total
205     $origin.                      #Company Identification (Immediate Origin)
206     (' 'x19).                     #Message Authentication Code (19 char blank)
207     (' 'x6).                      #Federal Reserve Use (6 char blank)
208     substr($dest, 0, 8).          #Originating Financial Institution
209     $batchnum. "\n".              #Batch Number ("number batches sequentially")
210
211     ###
212     # File Control Record
213     ###
214
215     '9'.                                 #Record Type Code
216     '000001'.                            #Batch Counter (# of batch header recs)
217     sprintf('%06d', $blocks).            #num of physical blocks on the file
218     sprintf('%08d', $batchcount).        #total # of entry detail and addenda
219     $entry_hash.
220     sprintf('%012.0f', $batchtotal * 100). #Debit total
221     '000000000000'.                      #Credit total
222     ( ' 'x39 ).                          #Reserved / blank
223
224     ###
225     # Pad with 9999 records to blocks of 10
226     ###
227
228     $fill
229
230   },
231
232 );
233
234 1;
235