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