paxmex 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Lumos Labs, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ [![Build Status](https://travis-ci.org/lumoslabs/paxmex.png)](https://travis-ci.org/lumoslabs/paxmex)
2
+
3
+ # Paxmex
4
+
5
+ This gem parses your Amex data files into human readable data.
6
+
7
+ ## Installation
8
+
9
+ ### From RubyGems.org
10
+
11
+ ```sh
12
+ % gem install paxmex
13
+ ```
14
+
15
+ ## Available Methods
16
+
17
+ * parse_eptrn(file_path)
18
+ * parse_epraw(file_path)
19
+
20
+ Both methods return a readable hash in the following format:
21
+
22
+ ```ruby
23
+ {
24
+ "DATA_FILE_TRAILER_RECORD" => {
25
+ "DF_TRL_RECORD_TYPE" => "DFTRL",
26
+ "DF_TRL_DATE" => #<Date: 2013-04-05>,
27
+ "DF_TRL_TIME" => "0435",
28
+ "DF_TRL_FILE_ID" => 0,
29
+ "DF_TRL_FILE_NAME" => "LUMOS LABS INC",
30
+ "DF_TRL_RECIPIENT_KEY" => "00000000002754170029 0000000000",
31
+ "DF_TRL_RECORD_COUNT" => 4
32
+ },
33
+ "DATA_FILE_HEADER_RECORD" => {
34
+ "DF_HDR_RECORD_TYPE" => "DFHDR",
35
+ "DF_HDR_DATE" => #<Date: 2013-04-05>,
36
+ "DF_HDR_TIME" => "0435",
37
+ "DF_HDR_FILE_ID" => 0,
38
+ "DF_HDR_FILE_NAME" => "LUMOS LABS INC"
39
+ },
40
+ ...
41
+ }
42
+ ```
43
+
44
+ Values are parsed from their representation into a corresponding native Ruby type:
45
+
46
+ * Alphanumeric values become strings (with whitespace stripped)
47
+ * Numeric values become Fixnums
48
+ * Julian strings become Date objects
49
+ * Date strings become Date objects
50
+ * Alphanumerically represented decimals become BigDecimal objects (e.g: ```'0000000011A' -> BigDecimal.new(1.11, 7)```)
51
+
52
+ If you'd like the raw values to be returned instead, you can set the ```raw_values``` option to true, e.g.:
53
+
54
+ ```ruby
55
+ Paxmex.parse_eptrn(path_to_file, raw_values: true)
56
+ Paxmex.parse_epraw(path_to_file, raw_values: true)
57
+ ```
58
+
59
+
60
+ ## Example
61
+
62
+ ```ruby
63
+ require 'paxmex'
64
+ Paxmex.parse_eptrn('/path/to/amex/eptrn/raw/file')
65
+ Paxmex.parse_epraw('/path/to/amex/epraw/raw/file')
66
+ ```
67
+
68
+ The input files for either methods is a data report file provided by American Express. These files are in either EPRAW or EPTRN format so use the relevant method to parse them. We have provided dummy EPRAW and EPTRN files in `spec/support`. Output and key-value pairs vary depending on whether you choose to parse an EPTRN or EPRAW file.
69
+
70
+ ## Contributing
71
+
72
+ Fork and submit a pull request and make sure you add a test for any feature you add.
73
+
74
+ ## License
75
+
76
+ LICENSE: (The MIT License)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ require 'rspec/core/rake_task'
9
+
10
+ RSpec::Core::RakeTask.new(:spec)
11
+
12
+ task :default => :spec
data/config/epraw.yml ADDED
@@ -0,0 +1,307 @@
1
+ DATA_FILE_HEADER_RECORD:
2
+ FIELDS:
3
+ - NAME: DF_HDR_RECORD_TYPE
4
+ RANGE: [0, 4]
5
+ - NAME: DF_HDR_DATE
6
+ RANGE: [5, 12]
7
+ TYPE: date
8
+ - NAME: DF_HDR_TIME
9
+ RANGE: [13, 16]
10
+ - NAME: DF_HDR_FILE_ID
11
+ RANGE: [17, 22]
12
+ TYPE: numeric
13
+ - NAME: DF_HDR_FILE_NAME
14
+ RANGE: [23, 42]
15
+ DETAIL_RECORD:
16
+ ABSTRACT: true
17
+ RECURRING: true
18
+ TYPE_FIELD: [43, 44]
19
+ TYPE_MAPPING:
20
+ '00': SUMMARY_RECORD
21
+ '10': SUMMARY_OF_CHARGE_DETAIL_RECORD
22
+ '20': CHARGEBACK_DETAIL_RECORD
23
+ '30': ADJUSTMENT_DETAIL_RECORD
24
+ '50': OTHER_FEES_AND_REVENUES_DETAIL_RECORDS
25
+ TYPES:
26
+ SUMMARY_RECORD:
27
+ RECURRING: true
28
+ FIELDS:
29
+ - NAME: AMEX_PAYEE_NUMBER
30
+ RANGE: [0, 9]
31
+ TYPE: numeric
32
+ - NAME: AMEX_SORT_FIELD_1
33
+ RANGE: [10, 19]
34
+ TYPE: numeric
35
+ - NAME: AMEX_SORT_FIELD_2
36
+ RANGE: [20, 29]
37
+ TYPE: numeric
38
+ - NAME: PAYMENT_YEAR
39
+ RANGE: [30, 33]
40
+ TYPE: numeric
41
+ - NAME: PAYMENT_NUMBER
42
+ RANGE: [34, 41]
43
+ - NAME: RECORD_TYPE
44
+ RANGE: [42, 42]
45
+ - NAME: DETAIL_RECORD_TYPE
46
+ RANGE: [43, 44]
47
+ - NAME: PAYMENT_DATE
48
+ RANGE: [45, 51]
49
+ TYPE: julian
50
+ - NAME: PAYMENT_AMOUNT
51
+ RANGE: [52, 62]
52
+ TYPE: decimal
53
+ - NAME: DEBIT_BALANCE_AMOUNT
54
+ RANGE: [63, 71]
55
+ TYPE: decimal
56
+ - NAME: ABA_BANK_NUMBER
57
+ RANGE: [72, 80]
58
+ TYPE: numeric
59
+ - NAME: SE_DDA_NUMBER
60
+ RANGE: [81, 97]
61
+ SUMMARY_OF_CHARGE_DETAIL_RECORD:
62
+ RECURRING: true
63
+ FIELDS:
64
+ - NAME: AMEX_PAYEE_NUMBER
65
+ RANGE: [0, 9]
66
+ TYPE: numeric
67
+ - NAME: AMEX_SE_NUMBER
68
+ RANGE: [10, 19]
69
+ TYPE: numeric
70
+ - NAME: SE_UNIT_NUMBER
71
+ RANGE: [20, 29]
72
+ - NAME: PAYMENT_YEAR
73
+ RANGE: [30, 33]
74
+ TYPE: numeric
75
+ - NAME: PAYMENT_NUMBER
76
+ RANGE: [34, 41]
77
+ - NAME: RECORD_TYPE
78
+ RANGE: [42, 42]
79
+ - NAME: DETAIL_RECORD_TYPE
80
+ RANGE: [43, 44]
81
+ - NAME: SE_BUSINESS_DATE
82
+ RANGE: [45, 51]
83
+ TYPE: julian
84
+ - NAME: AMEX_PROCESS_DATE
85
+ RANGE: [52, 58]
86
+ TYPE: julian
87
+ - NAME: SOC_INVOICE_NUMBER
88
+ RANGE: [59, 64]
89
+ TYPE: numeric
90
+ - NAME: SOC_AMOUNT
91
+ RANGE: [65, 75]
92
+ TYPE: decimal
93
+ - NAME: DISCOUNT_AMOUNT
94
+ RANGE: [76, 84]
95
+ TYPE: decimal
96
+ - NAME: SERVICE_FEE_AMOUNT
97
+ RANGE: [85, 91]
98
+ TYPE: decimal
99
+ - NAME: OPTIMA_DIVIDEND_AMOUNT
100
+ RANGE: [92, 98]
101
+ TYPE: decimal
102
+ - NAME: NET_SOC_AMOUNT
103
+ RANGE: [99, 109]
104
+ TYPE: decimal
105
+ - NAME: DISCOUNT_RATE
106
+ RANGE: [110, 114]
107
+ TYPE: numeric
108
+ - NAME: SERVICE_FEE_RATE
109
+ RANGE: [115, 119]
110
+ TYPE: numeric
111
+ - NAME: OPTIMA_DIVIDEND_RATE
112
+ RANGE: [120, 124]
113
+ TYPE: numeric
114
+ - NAME: OPTIMA_GROSS_AMOUNT
115
+ RANGE: [125, 135]
116
+ TYPE: decimal
117
+ - NAME: OPTIMA_ROC_COUNT
118
+ RANGE: [136, 140]
119
+ - NAME: AMEX_GROSS_AMOUNT
120
+ RANGE: [141, 151]
121
+ TYPE: decimal
122
+ - NAME: AMEX_ROC_COUNT
123
+ RANGE: [152, 156]
124
+ - NAME: TRACKING_ID
125
+ RANGE: [157, 165]
126
+ TYPE: numeric
127
+ - NAME: CPC_INDICATOR
128
+ RANGE: [166, 166]
129
+ CHARGEBACK_DETAIL_RECORD:
130
+ RECURRING: true
131
+ FIELDS:
132
+ - NAME: AMEX_PAYEE_NUMBER
133
+ RANGE: [0, 9]
134
+ TYPE: numeric
135
+ - NAME: AMEX_SE_NUMBER
136
+ RANGE: [10, 19]
137
+ TYPE: numeric
138
+ - NAME: SE_UNIT_NUMBER
139
+ RANGE: [20, 29]
140
+ - NAME: PAYMENT_YEAR
141
+ RANGE: [30, 33]
142
+ TYPE: numeric
143
+ - NAME: PAYMENT_NUMBER
144
+ RANGE: [34, 41]
145
+ - NAME: RECORD_TYPE
146
+ RANGE: [42, 42]
147
+ - NAME: DETAIL_RECORD_TYPE
148
+ RANGE: [43, 44]
149
+ - NAME: SE_BUSINESS_DATE
150
+ RANGE: [45, 51]
151
+ TYPE: julian
152
+ - NAME: AMEX_PROCESS_DATE
153
+ RANGE: [52, 58]
154
+ TYPE: julian
155
+ - NAME: SOC_INVOICE_NUMBER
156
+ RANGE: [59, 64]
157
+ TYPE: numeric
158
+ - NAME: SOC_AMOUNT
159
+ RANGE: [65, 75]
160
+ TYPE: decimal
161
+ - NAME: CHARGEBACK_AMOUNT
162
+ RANGE: [76, 84]
163
+ TYPE: decimal
164
+ - NAME: DISCOUNT_AMOUNT
165
+ RANGE: [85, 93]
166
+ TYPE: decimal
167
+ - NAME: SERVICE_FEE_AMOUNT
168
+ RANGE: [94, 100]
169
+ TYPE: decimal
170
+ - NAME: OPTIMA_DIVIDEND_AMOUNT
171
+ RANGE: [101, 107]
172
+ TYPE: decimal
173
+ - NAME: NET_CHARGEBACK_AMOUNT
174
+ RANGE: [108, 116]
175
+ TYPE: decimal
176
+ - NAME: DISCOUNT_RATE
177
+ RANGE: [117, 121]
178
+ TYPE: numeric
179
+ - NAME: SERVICE_FEE_RATE
180
+ RANGE: [122, 126]
181
+ TYPE: numeric
182
+ - NAME: OPTIMA_DIVIDEND_RATE
183
+ RANGE: [127, 131]
184
+ TYPE: numeric
185
+ - NAME: OPTIMA_GROSS_AMOUNT
186
+ RANGE: [132, 142]
187
+ TYPE: decimal
188
+ - NAME: CHARGEBACK_REASON
189
+ RANGE: [143, 422]
190
+ ADJUSTMENT_DETAIL_RECORD:
191
+ RECURRING: true
192
+ FIELDS:
193
+ - NAME: AMEX_PAYEE_NUMBER
194
+ RANGE: [0, 9]
195
+ TYPE: numeric
196
+ - NAME: AMEX_SE_NUMBER
197
+ RANGE: [10, 19]
198
+ TYPE: numeric
199
+ - NAME: SE_UNIT_NUMBER
200
+ RANGE: [20, 29]
201
+ - NAME: PAYMENT_YEAR
202
+ RANGE: [30, 33]
203
+ TYPE: numeric
204
+ - NAME: PAYMENT_NUMBER
205
+ RANGE: [34, 41]
206
+ - NAME: RECORD_TYPE
207
+ RANGE: [42, 42]
208
+ - NAME: DETAIL_RECORD_TYPE
209
+ RANGE: [43, 44]
210
+ - NAME: AMEX_PROCESS_DATE
211
+ RANGE: [45, 51]
212
+ TYPE: julian
213
+ - NAME: ADJUSTMENT_NUMBER
214
+ RANGE: [52, 57]
215
+ TYPE: numeric
216
+ - NAME: ADJUSTMENT_AMOUNT
217
+ RANGE: [58, 66]
218
+ TYPE: decimal
219
+ - NAME: DISCOUNT_AMOUNT
220
+ RANGE: [67, 75]
221
+ TYPE: decimal
222
+ - NAME: SERVICE_FEE_AMOUNT
223
+ RANGE: [76, 82]
224
+ TYPE: decimal
225
+ - NAME: OPTIMA_DIVIDEND_AMOUNT
226
+ RANGE: [83, 89]
227
+ TYPE: decimal
228
+ - NAME: NET_ADJUSTMENT_AMOUNT
229
+ RANGE: [90, 98]
230
+ TYPE: decimal
231
+ - NAME: DISCOUNT_RATE
232
+ RANGE: [99, 103]
233
+ TYPE: numeric
234
+ - NAME: SERVICE_FEE_RATE
235
+ RANGE: [104, 108]
236
+ TYPE: numeric
237
+ - NAME: OPTIMA_DIVIDEND_RATE
238
+ RANGE: [109, 113]
239
+ TYPE: numeric
240
+ - NAME: OPTIMA_GROSS_AMOUNT
241
+ RANGE: [114, 124]
242
+ TYPE: decimal
243
+ - NAME: CARDMEMBER_NUMBER
244
+ RANGE: [125, 141]
245
+ TYPE: numeric
246
+ - NAME: ADJUSTMENT_REASON
247
+ RANGE: [142, 421]
248
+ OTHER_FEES_AND_REVENUES_DETAIL_RECORDS:
249
+ RECURRING: true
250
+ FIELDS:
251
+ - NAME: AMEX_PAYEE_NUMBER
252
+ RANGE: [0, 9]
253
+ TYPE: numeric
254
+ - NAME: AMEX_SE_NUMBER
255
+ RANGE: [10, 19]
256
+ TYPE: numeric
257
+ - NAME: SE_UNIT_NUMBER
258
+ RANGE: [20, 29]
259
+ - NAME: PAYMENT_YEAR
260
+ RANGE: [30, 33]
261
+ TYPE: numeric
262
+ - NAME: PAYMENT_NUMBER
263
+ RANGE: [34, 41]
264
+ - NAME: RECORD_TYPE
265
+ RANGE: [42, 42]
266
+ - NAME: DETAIL_RECORD_TYPE
267
+ RANGE: [43, 44]
268
+ - NAME: AMEX_PROCESS_DATE
269
+ RANGE: [45, 51]
270
+ TYPE: julian
271
+ - NAME: ASSET_BILLING_AMOUNT
272
+ RANGE: [52, 60]
273
+ TYPE: decimal
274
+ - NAME: ASSET_BILLING_DESCRIPTION
275
+ RANGE: [61, 126]
276
+ - NAME: TAKE_ONE_COMMISSION_AMOUNT
277
+ RANGE: [126, 134]
278
+ TYPE: decimal
279
+ - NAME: TAKE_ONE_DESCRIPTION
280
+ RANGE: [135, 214]
281
+ - NAME: OTHER_FEE_AMOUNT
282
+ RANGE: [215, 223]
283
+ TYPE: decimal
284
+ - NAME: OTHER_FEE_DESCRIPTION
285
+ RANGE: [224, 303]
286
+ - NAME: ASSET_BILLING_TAX
287
+ RANGE: [304, 312]
288
+ TYPE: decimal
289
+ DATA_FILE_TRAILER_RECORD:
290
+ TRAILER: true
291
+ FIELDS:
292
+ - NAME: DF_TRL_RECORD_TYPE
293
+ RANGE: [0, 4]
294
+ - NAME: DF_TRL_DATE
295
+ RANGE: [5, 12]
296
+ TYPE: date
297
+ - NAME: DF_TRL_TIME
298
+ RANGE: [13, 16]
299
+ - NAME: DF_TRL_FILE_ID
300
+ RANGE: [17, 22]
301
+ - NAME: DF_TRL_FILE_NAME
302
+ RANGE: [23, 42]
303
+ - NAME: DF_TRL_RECIPIENT_KEY
304
+ RANGE: [43, 82]
305
+ - NAME: DF_TRL_RECORD_COUNT
306
+ RANGE: [83, 89]
307
+ TYPE: numeric
data/config/eptrn.yml ADDED
@@ -0,0 +1,344 @@
1
+ DATA_FILE_HEADER_RECORD:
2
+ FIELDS:
3
+ - NAME: DF_HDR_RECORD_TYPE
4
+ RANGE: [0, 4]
5
+ - NAME: DF_HDR_DATE
6
+ RANGE: [5, 12]
7
+ TYPE: date
8
+ - NAME: DF_HDR_TIME
9
+ RANGE: [13, 16]
10
+ - NAME: DF_HDR_FILE_ID
11
+ RANGE: [17, 22]
12
+ TYPE: numeric
13
+ - NAME: DF_HDR_FILE_NAME
14
+ RANGE: [23, 42]
15
+ DETAIL_RECORD:
16
+ ABSTRACT: true
17
+ RECURRING: true
18
+ TYPE_FIELD: [43, 44]
19
+ TYPE_MAPPING:
20
+ '00': SUMMARY_RECORD
21
+ '10': SUMMARY_OF_CHARGE_DETAIL_RECORD
22
+ '11': RECORD_OF_CHARGE_DETAIL_RECORD
23
+ '20': CHARGEBACK_DETAIL_RECORD
24
+ '30': ADJUSTMENT_DETAIL_RECORD
25
+ '50': OTHER_FEES_AND_REVENUES_DETAIL_RECORDS
26
+ TYPES:
27
+ SUMMARY_RECORD:
28
+ RECURRING: true
29
+ FIELDS:
30
+ - NAME: AMEX_PAYEE_NUMBER
31
+ RANGE: [0, 9]
32
+ TYPE: numeric
33
+ - NAME: AMEX_SORT_FIELD_1
34
+ RANGE: [10, 19]
35
+ - NAME: AMEX_SORT_FIELD_2
36
+ RANGE: [20, 29]
37
+ - NAME: PAYMENT_YEAR
38
+ RANGE: [30, 33]
39
+ TYPE: numeric
40
+ - NAME: PAYMENT_NUMBER
41
+ RANGE: [34, 41]
42
+ - NAME: RECORD_TYPE
43
+ RANGE: [42, 42]
44
+ - NAME: DETAIL_RECORD_TYPE
45
+ RANGE: [43, 44]
46
+ - NAME: PAYMENT_DATE
47
+ RANGE: [45, 51]
48
+ TYPE: julian
49
+ - NAME: PAYMENT_AMOUNT
50
+ RANGE: [52, 62]
51
+ TYPE: decimal
52
+ - NAME: DEBIT_BALANCE_AMOUNT
53
+ RANGE: [63, 71]
54
+ TYPE: decimal
55
+ - NAME: ABA_BANK_NUMBER
56
+ RANGE: [72, 80]
57
+ TYPE: numeric
58
+ - NAME: SE_DDA_NUMBER
59
+ RANGE: [81, 97]
60
+ SUMMARY_OF_CHARGE_DETAIL_RECORD:
61
+ RECURRING: true
62
+ FIELDS:
63
+ - NAME: AMEX_PAYEE_NUMBER
64
+ RANGE: [0, 9]
65
+ TYPE: numeric
66
+ - NAME: AMEX_SE_NUMBER
67
+ RANGE: [10, 19]
68
+ TYPE: numeric
69
+ - NAME: SE_UNIT_NUMBER
70
+ RANGE: [20, 29]
71
+ - NAME: PAYMENT_YEAR
72
+ RANGE: [30, 33]
73
+ TYPE: numeric
74
+ - NAME: PAYMENT_NUMBER
75
+ RANGE: [34, 41]
76
+ - NAME: RECORD_TYPE
77
+ RANGE: [42, 42]
78
+ - NAME: DETAIL_RECORD_TYPE
79
+ RANGE: [43, 44]
80
+ - NAME: SE_BUSINESS_DATE
81
+ RANGE: [45, 51]
82
+ TYPE: julian
83
+ - NAME: AMEX_PROCESS_DATE
84
+ RANGE: [52, 58]
85
+ TYPE: julian
86
+ - NAME: SOC_INVOICE_NUMBER
87
+ RANGE: [59, 64]
88
+ TYPE: numeric
89
+ - NAME: SOC_AMOUNT
90
+ RANGE: [65, 75]
91
+ TYPE: decimal
92
+ - NAME: DISCOUNT_AMOUNT
93
+ RANGE: [76, 84]
94
+ TYPE: decimal
95
+ - NAME: SERVICE_FEE_AMOUNT
96
+ RANGE: [85, 91]
97
+ TYPE: decimal
98
+ - NAME: NET_SOC_AMOUNT
99
+ RANGE: [99, 109]
100
+ TYPE: decimal
101
+ - NAME: DISCOUNT_RATE
102
+ RANGE: [110, 114]
103
+ TYPE: numeric
104
+ - NAME: SERVICE_FEE_RATE
105
+ RANGE: [115, 119]
106
+ TYPE: numeric
107
+ - NAME: AMEX_GROSS_AMOUNT
108
+ RANGE: [141, 151]
109
+ TYPE: decimal
110
+ - NAME: AMEX_ROC_COUNT
111
+ RANGE: [152, 156]
112
+ - NAME: TRACKING_ID
113
+ RANGE: [157, 165]
114
+ TYPE: numeric
115
+ - NAME: CPC_INDICATOR
116
+ RANGE: [166, 166]
117
+ - NAME: AMEX_RO_COUNT_POA
118
+ RANGE: [182, 188]
119
+ TYPE: decimal
120
+ RECORD_OF_CHARGE_DETAIL_RECORD:
121
+ RECURRING: true
122
+ FIELDS:
123
+ - NAME: TLRR_AMEX_PAYEE_NUMBER
124
+ RANGE: [0, 9]
125
+ TYPE: numeric
126
+ - NAME: TLRR_AMEX_SE_NUMBER
127
+ RANGE: [10, 19]
128
+ TYPE: numeric
129
+ - NAME: TLRR_SE_UNIT_NUMBER
130
+ RANGE: [20, 29]
131
+ - NAME: TLRR_PAYMENT_YEAR
132
+ RANGE: [30, 33]
133
+ TYPE: numeric
134
+ - NAME: TLRR_PAYMENT_NUMBER
135
+ RANGE: [34, 41]
136
+ - NAME: TLRR_RECORD_TYPE
137
+ RANGE: [42, 42]
138
+ - NAME: TLRR_DETAIL_RECORD_TYPE
139
+ RANGE: [43, 44]
140
+ - NAME: TLRR_SE_BUSINESS_DATE
141
+ RANGE: [45, 51]
142
+ TYPE: julian
143
+ - NAME: TLRR_AMEX_PROCESS_DATE
144
+ RANGE: [52, 58]
145
+ TYPE: julian
146
+ - NAME: TLRR_SOC_INVOICE_NUMBER
147
+ RANGE: [59, 64]
148
+ TYPE: numeric
149
+ - NAME: TLRR_SOC_AMOUNT
150
+ RANGE: [65, 77]
151
+ TYPE: decimal
152
+ - NAME: TLRR_ROC_AMOUNT
153
+ RANGE: [78, 90]
154
+ TYPE: decimal
155
+ - NAME: TLRR_CM_NUMBER
156
+ RANGE: [91, 105]
157
+ TYPE: numeric
158
+ - NAME: TLRR_CM_REF_NO
159
+ RANGE: [106, 116]
160
+ - NAME: TLRR_SE_REF
161
+ RANGE: [117, 125]
162
+ - NAME: TLRR_SE_REF_EXPANSION_FILLER
163
+ RANGE: [126, 135]
164
+ - NAME: TLRR_ROC_NUMBER
165
+ RANGE: [136, 145]
166
+ - NAME: TLRR_TRAN_DATE
167
+ RANGE: [146, 152]
168
+ TYPE: julian
169
+ - NAME: TLRR_SE_REF_POA
170
+ RANGE: [153, 182]
171
+ - NAME: NON_COMPLIANT_INDICATOR
172
+ RANGE: [183, 183]
173
+ - NAME: NON_COMPLIANT_ERROR_CODE_1
174
+ RANGE: [184, 187]
175
+ - NAME: NON_COMPLIANT_ERROR_CODE_2
176
+ RANGE: [188, 191]
177
+ - NAME: NON_COMPLIANT_ERROR_CODE_3
178
+ RANGE: [192, 195]
179
+ - NAME: NON_COMPLIANT_ERROR_CODE_4
180
+ RANGE: [196, 199]
181
+ - NAME: NON_SWIPED_INDICATOR
182
+ RANGE: [200, 200]
183
+ CHARGEBACK_DETAIL_RECORD:
184
+ RECURRING: true
185
+ FIELDS:
186
+ - NAME: AMEX_PAYEE_NUMBER
187
+ RANGE: [0, 9]
188
+ TYPE: numeric
189
+ - NAME: AMEX_SE_NUMBER
190
+ RANGE: [10, 19]
191
+ TYPE: numeric
192
+ - NAME: SE_UNIT_NUMBER
193
+ RANGE: [20, 29]
194
+ - NAME: PAYMENT_YEAR
195
+ RANGE: [30, 33]
196
+ TYPE: numeric
197
+ - NAME: PAYMENT_NUMBER
198
+ RANGE: [34, 41]
199
+ - NAME: RECORD_TYPE
200
+ RANGE: [42, 42]
201
+ - NAME: DETAIL_RECORD_TYPE
202
+ RANGE: [43, 44]
203
+ - NAME: SE_BUSINESS_DATE
204
+ RANGE: [45, 51]
205
+ TYPE: julian
206
+ - NAME: AMEX_PROCESS_DATE
207
+ RANGE: [52, 58]
208
+ TYPE: julian
209
+ - NAME: SOC_INVOICE_NUMBER
210
+ RANGE: [59, 64]
211
+ TYPE: numeric
212
+ - NAME: SOC_AMOUNT
213
+ RANGE: [65, 75]
214
+ TYPE: decimal
215
+ - NAME: CHARGEBACK_AMOUNT
216
+ RANGE: [76, 84]
217
+ TYPE: decimal
218
+ - NAME: DISCOUNT_AMOUNT
219
+ RANGE: [85, 93]
220
+ TYPE: decimal
221
+ - NAME: SERVICE_FEE_AMOUNT
222
+ RANGE: [94, 100]
223
+ TYPE: decimal
224
+ - NAME: NET_CHARGEBACK_AMOUNT
225
+ RANGE: [108, 116]
226
+ TYPE: decimal
227
+ - NAME: DISCOUNT_RATE
228
+ RANGE: [117, 121]
229
+ TYPE: numeric
230
+ - NAME: SERVICE_FEE_RATE
231
+ RANGE: [122, 126]
232
+ TYPE: numeric
233
+ - NAME: CHARGEBACK_REASON
234
+ RANGE: [143, 422]
235
+ ADJUSTMENT_DETAIL_RECORD:
236
+ RECURRING: true
237
+ FIELDS:
238
+ - NAME: AMEX_PAYEE_NUMBER
239
+ RANGE: [0, 9]
240
+ TYPE: numeric
241
+ - NAME: AMEX_SE_NUMBER
242
+ RANGE: [10, 19]
243
+ TYPE: numeric
244
+ - NAME: SE_UNIT_NUMBER
245
+ RANGE: [20, 29]
246
+ - NAME: PAYMENT_YEAR
247
+ RANGE: [30, 33]
248
+ TYPE: numeric
249
+ - NAME: PAYMENT_NUMBER
250
+ RANGE: [34, 41]
251
+ - NAME: RECORD_TYPE
252
+ RANGE: [42, 42]
253
+ - NAME: DETAIL_RECORD_TYPE
254
+ RANGE: [43, 44]
255
+ - NAME: AMEX_PROCESS_DATE
256
+ RANGE: [45, 51]
257
+ TYPE: julian
258
+ - NAME: ADJUSTMENT_NUMBER
259
+ RANGE: [52, 57]
260
+ TYPE: numeric
261
+ - NAME: ADJUSTMENT_AMOUNT
262
+ RANGE: [58, 66]
263
+ TYPE: decimal
264
+ - NAME: DISCOUNT_AMOUNT
265
+ RANGE: [67, 75]
266
+ TYPE: decimal
267
+ - NAME: SERVICE_FEE_AMOUNT
268
+ RANGE: [76, 82]
269
+ TYPE: decimal
270
+ - NAME: NET_ADJUSTMENT_AMOUNT
271
+ RANGE: [90, 98]
272
+ TYPE: decimal
273
+ - NAME: DISCOUNT_RATE
274
+ RANGE: [99, 103]
275
+ TYPE: numeric
276
+ - NAME: SERVICE_FEE_RATE
277
+ RANGE: [104, 108]
278
+ TYPE: numeric
279
+ - NAME: CARDMEMBER_NUMBER
280
+ RANGE: [125, 141]
281
+ TYPE: numeric
282
+ - NAME: ADJUSTMENT_REASON
283
+ RANGE: [142, 421]
284
+ OTHER_FEES_AND_REVENUES_DETAIL_RECORDS:
285
+ RECURRING: true
286
+ FIELDS:
287
+ - NAME: AMEX_PAYEE_NUMBER
288
+ RANGE: [0, 9]
289
+ TYPE: numeric
290
+ - NAME: AMEX_SE_NUMBER
291
+ RANGE: [10, 19]
292
+ TYPE: numeric
293
+ - NAME: SE_UNIT_NUMBER
294
+ RANGE: [20, 29]
295
+ - NAME: PAYMENT_YEAR
296
+ RANGE: [30, 33]
297
+ TYPE: numeric
298
+ - NAME: PAYMENT_NUMBER
299
+ RANGE: [34, 41]
300
+ - NAME: RECORD_TYPE
301
+ RANGE: [42, 42]
302
+ - NAME: DETAIL_RECORD_TYPE
303
+ RANGE: [43, 44]
304
+ - NAME: AMEX_PROCESS_DATE
305
+ RANGE: [45, 51]
306
+ TYPE: julian
307
+ - NAME: ASSET_BILLING_AMOUNT
308
+ RANGE: [52, 60]
309
+ TYPE: decimal
310
+ - NAME: ASSET_BILLING_DESCRIPTION
311
+ RANGE: [61, 126]
312
+ - NAME: TAKE_ONE_COMMISSION_AMOUNT
313
+ RANGE: [126, 134]
314
+ TYPE: decimal
315
+ - NAME: TAKE_ONE_DESCRIPTION
316
+ RANGE: [135, 214]
317
+ - NAME: OTHER_FEE_AMOUNT
318
+ RANGE: [215, 223]
319
+ TYPE: decimal
320
+ - NAME: OTHER_FEE_DESCRIPTION
321
+ RANGE: [224, 303]
322
+ - NAME: ASSET_BILLING_TAX
323
+ RANGE: [304, 312]
324
+ TYPE: decimal
325
+ DATA_FILE_TRAILER_RECORD:
326
+ TRAILER: true
327
+ FIELDS:
328
+ - NAME: DF_TRL_RECORD_TYPE
329
+ RANGE: [0, 4]
330
+ - NAME: DF_TRL_DATE
331
+ RANGE: [5, 12]
332
+ TYPE: date
333
+ - NAME: DF_TRL_TIME
334
+ RANGE: [13, 16]
335
+ - NAME: DF_TRL_FILE_ID
336
+ RANGE: [17, 22]
337
+ TYPE: numeric
338
+ - NAME: DF_TRL_FILE_NAME
339
+ RANGE: [23, 42]
340
+ - NAME: DF_TRL_RECIPIENT_KEY
341
+ RANGE: [43, 82]
342
+ - NAME: DF_TRL_RECORD_COUNT
343
+ RANGE: [83, 89]
344
+ TYPE: numeric
@@ -0,0 +1,76 @@
1
+ require 'yaml'
2
+ require 'paxmex/schema'
3
+
4
+ class Paxmex::Parser
5
+ SCHEMATA = %w(epraw eptrn).reduce({}) do |h, fn|
6
+ file = File.expand_path("../../config/#{fn}.yml", File.dirname(__FILE__))
7
+ h.merge(fn => Paxmex::Schema.new(YAML::load(File.open(file))))
8
+ end
9
+
10
+ attr_reader :schema
11
+
12
+ def initialize(path, opts = {})
13
+ @path = path
14
+ @schema = SCHEMATA[opts[:schema]]
15
+ end
16
+
17
+ def raw
18
+ @raw ||= File.read(@path).chomp
19
+ end
20
+
21
+ def parse(opts = {})
22
+ return @parsed if @parsed
23
+
24
+ content = raw.split("\n")
25
+
26
+ # Parse the trailing section first so that we don't need
27
+ # to consider it when parsing recurring sections
28
+ trailer_section = schema.sections.detect(&:trailer?)
29
+ trailer_content = [content.slice!(-1)]
30
+ @parsed = parse_section(content: trailer_content, section: trailer_section, raw: opts[:raw_values])
31
+
32
+ schema.sections.reject(&:trailer?).each do |section|
33
+ @parsed.merge!(
34
+ parse_section(
35
+ content: section.recurring? ? content : [content.slice!(0)],
36
+ section: section,
37
+ raw: opts[:raw_values]))
38
+ end
39
+
40
+ @parsed
41
+ end
42
+
43
+ private
44
+
45
+ def parse_section(opts = {})
46
+ raise 'Content must be provided' unless content = opts[:content]
47
+ raise 'Section must be provided' unless section = opts[:section]
48
+
49
+ result = {}
50
+ abstract_section = section if section.abstract?
51
+
52
+ content.each do |section_content|
53
+ if abstract_section
54
+ start, final = abstract_section.type_field
55
+ section_type = section_content[start..final]
56
+ section = abstract_section.section_for_type(section_type)
57
+ end
58
+
59
+ result[section.key] ||= [] if section.recurring?
60
+
61
+ p = {}
62
+ section.fields.each do |field|
63
+ raw_value = section_content[field.start..field.final]
64
+ p[field.name] = opts[:raw] ? raw_value : field.parse(raw_value)
65
+ end
66
+
67
+ if section.recurring?
68
+ result[section.key] << p
69
+ else
70
+ result[section.key] = p
71
+ end
72
+ end
73
+
74
+ result
75
+ end
76
+ end
@@ -0,0 +1,42 @@
1
+ require 'bigdecimal'
2
+ require 'paxmex/schema'
3
+
4
+ class Paxmex::Schema::Field
5
+ attr_reader :name, :start, :final, :type
6
+
7
+ def initialize(opts = {})
8
+ @name = opts[:name]
9
+ @start = opts[:start]
10
+ @final = opts[:final]
11
+ @type = opts[:type] || 'string'
12
+ end
13
+
14
+ def parse(raw_value)
15
+ case type
16
+ when 'julian' then parse_julian_date(raw_value)
17
+ when 'date' then Date.strptime(raw_value, '%m%d%Y')
18
+ when 'numeric' then raw_value.strip.to_i
19
+ when 'decimal' then parse_decimal(raw_value)
20
+ else raw_value.strip
21
+ end
22
+ end
23
+
24
+ def parse_decimal(value)
25
+ is_credit = !!(value =~ /[JKLMNOPQR}]/)
26
+ value = value.gsub(/[ABCDEFGHIJKLMNOPQR{}]/,
27
+ 'A' => 1, 'B' => 2, 'C' => 3,
28
+ 'D' => 4, 'E' => 5, 'F' => 6,
29
+ 'G' => 7, 'H' => 8, 'I' => 9,
30
+ 'J' => 1, 'K' => 2, 'L' => 3,
31
+ 'M' => 4, 'N' => 5, 'O' => 6,
32
+ 'P' => 7, 'Q' => 8, 'R' => 9,
33
+ '{' => 0, '}' => 0)
34
+
35
+ parsed_value = value.to_i * (is_credit ? -1 : 1) / 100.0
36
+ BigDecimal.new(parsed_value.to_s, 7)
37
+ end
38
+
39
+ def parse_julian_date(date_string)
40
+ Date.ordinal(date_string[0..3].to_i, date_string[4..6].to_i)
41
+ end
42
+ end
@@ -0,0 +1,60 @@
1
+ require 'paxmex/schema/field'
2
+
3
+ class Paxmex::Schema::Section
4
+ BLOCK_LENGTH = 450
5
+
6
+ attr_reader :key, :data
7
+
8
+ def initialize(key, data)
9
+ @key = key
10
+ @data = data
11
+ end
12
+
13
+ def recurring?
14
+ !!data['RECURRING']
15
+ end
16
+
17
+ def abstract?
18
+ !!data['ABSTRACT']
19
+ end
20
+
21
+ def trailer?
22
+ !!data['TRAILER']
23
+ end
24
+
25
+ def fields
26
+ @fields ||= data['FIELDS'].map do |field|
27
+ Paxmex::Schema::Field.new(
28
+ name: field['NAME'],
29
+ start: field['RANGE'].first,
30
+ final: field['RANGE'].last,
31
+ type: field['TYPE'])
32
+ end
33
+ end
34
+
35
+ def types
36
+ data['TYPES']
37
+ end
38
+
39
+ def type_field
40
+ data['TYPE_FIELD']
41
+ end
42
+
43
+ def type_mapping
44
+ data['TYPE_MAPPING']
45
+ end
46
+
47
+ def section_for_type(type)
48
+ sections_for_types.detect { |t| t.key == type_mapping[type] }
49
+ end
50
+
51
+ def length
52
+ BLOCK_LENGTH
53
+ end
54
+
55
+ private
56
+
57
+ def sections_for_types
58
+ @sections_for_types ||= types.map { |k, v| self.class.new(k, v) }
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ class Paxmex::Schema
2
+ require 'paxmex/schema/section'
3
+
4
+ def initialize(schema_hash)
5
+ @schema_hash = schema_hash
6
+ end
7
+
8
+ def sections
9
+ @sections ||= @schema_hash.map { |k, v| Section.new(k, v) }
10
+ end
11
+
12
+ def to_h
13
+ @schema_hash
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module Paxmex
2
+ VERSION = "0.0.1"
3
+ end
data/lib/paxmex.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Paxmex
2
+ require 'paxmex/parser'
3
+
4
+ def self.parse_eptrn(file, opts = {})
5
+ Parser.new(file, schema: 'eptrn').parse(opts)
6
+ end
7
+
8
+ def self.parse_epraw(file)
9
+ Parser.new(file, schema: 'epraw').parse(opts)
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ desc "Explaining what the task does"
2
+ task :paxmex do
3
+ # Task goes here
4
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paxmex
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Daryl Yeo
9
+ - Anthony Zacharakis
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-04-05 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 2.12.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: 2.12.0
31
+ - !ruby/object:Gem::Dependency
32
+ name: rake
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: 10.0.3
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 10.0.3
47
+ description: This gem is a parser for American Express transaction reconciliation
48
+ files.
49
+ email:
50
+ - daryl@lumoslabs.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - config/epraw.yml
56
+ - config/eptrn.yml
57
+ - lib/paxmex/parser.rb
58
+ - lib/paxmex/schema/field.rb
59
+ - lib/paxmex/schema/section.rb
60
+ - lib/paxmex/schema.rb
61
+ - lib/paxmex/version.rb
62
+ - lib/paxmex.rb
63
+ - lib/tasks/paxmex_tasks.rake
64
+ - MIT-LICENSE
65
+ - Rakefile
66
+ - README.md
67
+ homepage: https://github.com/lumoslabs/paxmex
68
+ licenses: []
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 1.8.23
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: This gem parses your Amex data files into human readable data.
91
+ test_files: []
92
+ has_rdoc: