global_collect 0.1.5 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data/HISTORY +7 -0
  2. data/Rakefile +2 -1
  3. data/VERSION +1 -1
  4. data/examples/parse_collection_report.rb +13 -0
  5. data/examples/parse_financial_statement.rb +8 -0
  6. data/examples/parse_payment_report.rb +8 -0
  7. data/examples/set_payment.rb +1 -1
  8. data/global_collect.gemspec +38 -2
  9. data/lib/global_collect.rb +12 -0
  10. data/lib/global_collect/log_parsing/collection_report/appendix_report_file.rb +72 -0
  11. data/lib/global_collect/log_parsing/collection_report/fields.rb +161 -0
  12. data/lib/global_collect/log_parsing/collection_report/parser.rb +68 -0
  13. data/lib/global_collect/log_parsing/collection_report/report_file.rb +32 -0
  14. data/lib/global_collect/log_parsing/financial_statement/report_file.rb +96 -0
  15. data/lib/global_collect/log_parsing/payment_report/fields.rb +709 -0
  16. data/lib/global_collect/log_parsing/payment_report/parser.rb +105 -0
  17. data/lib/global_collect/log_parsing/payment_report/report_file.rb +31 -0
  18. data/lib/global_collect/responses/base.rb +0 -2
  19. data/lib/global_collect/test_helper.rb +8 -4
  20. data/spec/log_parsing/collection_report/appendix_report_file_spec.rb +96 -0
  21. data/spec/log_parsing/collection_report/parser_spec.rb +42 -0
  22. data/spec/log_parsing/collection_report/report_file_spec.rb +17 -0
  23. data/spec/log_parsing/financial_statement/report_file_spec.rb +51 -0
  24. data/spec/log_parsing/payment_report/parser_spec.rb +37 -0
  25. data/spec/log_parsing/payment_report/report_file_spec.rb +23 -0
  26. data/spec/support/55550141.wr1 +7 -0
  27. data/spec/support/555520100602.csv +4 -0
  28. data/spec/support/555555550145.mt1 +3 -0
  29. data/spec/support/FS55550148COMPANY.asc +12 -0
  30. metadata +64 -6
@@ -0,0 +1,105 @@
1
+ module GlobalCollect::LogParsing::PaymentReport
2
+ class Parser
3
+ def self.parse(file)
4
+ FixedWidth.parse(file, :payment_report)
5
+ end
6
+
7
+ private
8
+
9
+ RECORD_CATEGORY = {
10
+ "X" => :processed ,
11
+ "+" => :collected ,
12
+ "-" => :reversed ,
13
+ "I" => :information
14
+ }
15
+
16
+ RECORD_TYPE = {
17
+ "FH" => :file_header ,
18
+ "FT" => :file_trailer ,
19
+ "BH" => :batch_header ,
20
+ "BT" => :batch_trailer,
21
+ "ON" => :settlement ,
22
+ "RS" => :refusal ,
23
+ "RN" => :rejection ,
24
+ "CR" => :refund ,
25
+ "CB" => :chargeback
26
+ }
27
+
28
+ DATA_GROUPS = {
29
+ 0..21 => :order,
30
+ 22..34 => :payment,
31
+ 35..44 => :chargeback,
32
+ 45..51 => :refund
33
+ }
34
+ def self.data_group(index)
35
+ DATA_GROUPS.detect{|range, group| range.include?(index) }.last
36
+ end
37
+
38
+ DATE_LAMBDA = lambda {|d| Date.strptime(d, "%Y%m%d") }
39
+ def self.options_for_format(format)
40
+ case format
41
+ when "N" then { :align => :right, :parser => :to_i }
42
+ when "AN" then { :align => :left }
43
+ when "D" then { :parser => DATE_LAMBDA }
44
+ else raise ArgumentError.new("Unknown field format '#{format.inspect}'!")
45
+ end
46
+ end
47
+
48
+ SPACERS = ['reserved', 'filler']
49
+ def self.make_column(section, field, options={})
50
+ if SPACERS.include?(field[:name])
51
+ section.spacer(field[:length].to_i, ' ')
52
+ else
53
+ section.column(field[:name].to_sym, field[:length].to_i, options_for_format(field[:type]).merge(options))
54
+ end
55
+ end
56
+
57
+ DEFINITION = FixedWidth.define(:payment_report, :nil_blank => true) do |pr|
58
+ pr.template :record_info do |ri|
59
+ ri.record_category 1, :parser => lambda{|x| RECORD_CATEGORY[x] }
60
+ ri.record_type 2, :parser => lambda{|x| RECORD_TYPE[x] }
61
+ end
62
+ pr.template :header_trailer do |hf|
63
+ HEADER_TRAILER_FIELDS.each {|field| make_column(hf, field) }
64
+ end
65
+
66
+ pr.header do |h|
67
+ h.trap { |line| line[0,3] == 'IFH' }
68
+ h.template :record_info
69
+ h.template :header_trailer
70
+ end
71
+
72
+ pr.batch_header do |bh|
73
+ bh.trap { |line| line[0,3] == 'IBH' }
74
+ bh.template :record_info
75
+ BATCH_HEADER_FIELDS.each {|field| make_column(bh, field) }
76
+ end
77
+
78
+ pr.data_records(:optional => true) do |d|
79
+ d.trap { |line| line =~ /^[X,-,+]/ }
80
+ d.template :record_info
81
+ DATA_FIELDS.each_with_index do |field, index|
82
+ make_column(d, field, :group => data_group(index))
83
+ end
84
+ end
85
+
86
+ pr.information_records(:optional => true) do |info|
87
+ info.trap { |line| line[0,3] == 'ITM' }
88
+ info.template :record_info
89
+ INFORMATION_FIELDS.each {|field| make_column(info, field) }
90
+ end
91
+
92
+ pr.batch_trailer do |bt|
93
+ bt.trap { |line| line[0,3] == 'IBT' }
94
+ bt.template :record_info
95
+ BATCH_TRAILER_FIELDS.each {|field| make_column(bt, field) }
96
+ end
97
+
98
+ pr.trailer do |t|
99
+ t.trap { |line| line[0,3] == 'IFT' }
100
+ t.template :record_info
101
+ t.template :header_trailer
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,31 @@
1
+ module GlobalCollect::LogParsing::PaymentReport
2
+ class ReportFile
3
+ attr_reader :path, :account_id, :date, :environment, :data
4
+
5
+ # RG - Ch2 => Technical aspects => File name
6
+ # 4 chars numeric account id, 1 char numeric year, 3 char numeric day-of-year,
7
+ # extension /wrt/ if test, /wr\d/ if production (usually wr1).
8
+ FILENAME_FORMAT = /^(\d{4})(\d{1})(\d{3})\.wr(.)$/
9
+ YEAR_BASE_ADJUSTED = ( Date.today.year * 10 ) / 10
10
+ TEST_ENV_BYTE = 't'
11
+ def initialize(path)
12
+ @path = path
13
+ if File.basename(path) =~ FILENAME_FORMAT
14
+ @account_id = $1
15
+ @date = Date.ordinal(YEAR_BASE_ADJUSTED + $2.to_i, $3.to_i)
16
+ @environment = (TEST_ENV_BYTE == $4) ? :test : :production
17
+ else
18
+ raise ArgumentError.new("Unparseable path '#{path}'!")
19
+ end
20
+ end
21
+
22
+ def parse
23
+ begin
24
+ file = File.open(@path, "r")
25
+ @data = GlobalCollect::LogParsing::PaymentReport::Parser.parse(file)
26
+ ensure
27
+ file.close
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,3 @@
1
- require 'time'
2
-
3
1
  module GlobalCollect::Responses
4
2
  # WDL §4 specifies the generalized response
5
3
  # This base class is sufficient for many responses that don't include anything
@@ -1,7 +1,11 @@
1
1
  require 'fakeweb'
2
2
 
3
- def read_canned_response(filename)
4
- File.read(File.join(File.dirname(__FILE__), '..', '..', 'spec', 'support', filename))
3
+ def support_path(filename)
4
+ File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec', 'support', filename))
5
+ end
6
+
7
+ def read_support_file(filename)
8
+ File.read(support_path(filename))
5
9
  end
6
10
 
7
11
  def filename_for(success, action, version)
@@ -14,12 +18,12 @@ def filename_for(success, action, version)
14
18
  end
15
19
 
16
20
  def install_canned_response(service, env, auth_scheme, success, action, version)
17
- response_body = read_canned_response(filename_for(success, action, version))
21
+ response_body = read_support_file(filename_for(success, action, version))
18
22
  service_url = GlobalCollect::ApiClient.service_url(service, env, auth_scheme)
19
23
  FakeWeb.register_uri(:post, service_url, :body => response_body)
20
24
  end
21
25
 
22
26
  def parse_canned_response(success, action, version)
23
- response_body = read_canned_response(filename_for(success, action, version))
27
+ response_body = read_support_file(filename_for(success, action, version))
24
28
  GlobalCollect::Responses::Base.new(Crack::XML.parse(response_body), response_body)
25
29
  end
@@ -0,0 +1,96 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe "the appendix to the collection report file" do
4
+ it "should sort out the metadata from the file name" do
5
+ report = GlobalCollect::LogParsing::CollectionReport::AppendixReportFile.new("555520100602.csv")
6
+ report.account_id.should == "5555"
7
+ report.date.should == Date.strptime("20100602", "%Y%m%d")
8
+ report.environment.should == :production
9
+ end
10
+
11
+ it "should parse" do
12
+ report = GlobalCollect::LogParsing::CollectionReport::AppendixReportFile.new(support_path("555520100602.csv"))
13
+ report.parse
14
+ report.data.should_not be_blank
15
+ report.data.should == {
16
+ :data_records => [
17
+ {
18
+ :order_currency => "USD",
19
+ :merchant_reference => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
20
+ :order_amount => 41.05,
21
+ :invoice_number => "",
22
+ :payment_method => :credit_card,
23
+ :customer_id => "",
24
+ :merchant_id => 5555,
25
+ :credit_card_company => "ECMC",
26
+ :currency_due => "USD",
27
+ :filename_data_delivery => "a0110147.pg1",
28
+ :record_category => "+",
29
+ :cross_border_indicator => "N",
30
+ :amount_due => 41.05,
31
+ :payment_type => :payment,
32
+ :date_due => Date.strptime("20100602", "%Y%m%d"),
33
+ :record_type => :settlement,
34
+ :globalcollect_payment_reference => "206807294137"
35
+ },
36
+ {
37
+ :order_currency => "USD",
38
+ :merchant_reference => "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
39
+ :order_amount => 88.99,
40
+ :invoice_number => "",
41
+ :payment_method => :credit_card,
42
+ :customer_id => "",
43
+ :merchant_id => 5555,
44
+ :credit_card_company => "VISA",
45
+ :currency_due => "USD",
46
+ :filename_data_delivery => "a0110147.pg1",
47
+ :record_category => "+",
48
+ :cross_border_indicator => "N",
49
+ :amount_due => 88.99,
50
+ :payment_type => :payment,
51
+ :date_due => Date.strptime("20100602", "%Y%m%d"),
52
+ :record_type => :settlement,
53
+ :globalcollect_payment_reference => "206807294138"
54
+ },
55
+ {
56
+ :order_currency => "USD",
57
+ :merchant_reference => "cccccccccccccccccccccccccccccc",
58
+ :order_amount => -24.5,
59
+ :invoice_number => "",
60
+ :payment_method => :credit_card,
61
+ :customer_id => "",
62
+ :merchant_id => 5555,
63
+ :credit_card_company => "VISA",
64
+ :currency_due => "USD",
65
+ :filename_data_delivery => "a0110147.rg1",
66
+ :record_category => "-",
67
+ :cross_border_indicator => "N",
68
+ :amount_due => -24.5,
69
+ :payment_type => :refund_or_reversal,
70
+ :date_due => Date.strptime("20100602", "%Y%m%d"),
71
+ :record_type => :refund,
72
+ :globalcollect_payment_reference => "206806008113"
73
+ },
74
+ {
75
+ :order_currency => "USD",
76
+ :merchant_reference => "dddddddddddddddddddddddddddddd",
77
+ :order_amount => -77.83,
78
+ :invoice_number => "",
79
+ :payment_method => :credit_card,
80
+ :customer_id => "",
81
+ :merchant_id => 5555,
82
+ :credit_card_company => "ECMC",
83
+ :currency_due => "USD",
84
+ :filename_data_delivery => "a0110147.rg1",
85
+ :record_category => "-",
86
+ :cross_border_indicator => "N",
87
+ :amount_due => -77.83,
88
+ :payment_type => :refund_or_reversal,
89
+ :date_due => Date.strptime("20100602", "%Y%m%d"),
90
+ :record_type => :refund,
91
+ :globalcollect_payment_reference => "206806008114"
92
+ }
93
+ ]
94
+ }
95
+ end
96
+ end
@@ -0,0 +1,42 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe "the collection report parser" do
4
+ it "should parse" do
5
+ file = File.open(support_path("555555550145.mt1"), "r")
6
+ data = GlobalCollect::LogParsing::CollectionReport::Parser.parse(file)
7
+ data[:header].should_not be_nil
8
+ data[:header][:account_id].should == 5555
9
+ data[:header][:record_type].should == :file_header
10
+ data[:header][:date_production].should == Date.ordinal(2010, 145)
11
+ data[:header][:period_to].should == Date.ordinal(2010, 145)
12
+ data[:header][:period_from].should == Date.ordinal(2010, 145)
13
+
14
+ data[:trailer].should_not be_nil
15
+ data[:trailer][:account_id].should == 5555
16
+ data[:trailer][:record_type].should == :file_trailer
17
+ data[:trailer][:number_of_records].should == 3
18
+ data[:trailer][:date_production].should == Date.ordinal(2010, 145)
19
+ data[:trailer][:period_to].should == Date.ordinal(2010, 145)
20
+ data[:trailer][:period_from].should == Date.ordinal(2010, 145)
21
+
22
+ data[:data_records].should_not be_nil
23
+ data[:data_records].size.should == 1
24
+ data_record = data[:data_records].first
25
+ data_record[:report_date_from ].should == Date.ordinal(2010, 145)
26
+ data_record[:amount_paid ].should == 2384211
27
+ data_record[:report_date_to ].should == Date.ordinal(2010, 145)
28
+ data_record[:amount_paid_sign ].should == nil
29
+ data_record[:currency_due ].should == "USD"
30
+ data_record[:exchange_rate ].should == 1.0
31
+ data_record[:match_date ].should == Date.ordinal(2010, 145)
32
+ data_record[:record_type ].should == :data_record
33
+ data_record[:amount_due ].should == 2384211
34
+ data_record[:rate_units ].should == 1
35
+ data_record[:report_year ].should == 2010
36
+ data_record[:amount_due_sign ].should == nil
37
+ data_record[:merchant_id ].should == 5555
38
+ data_record[:number_of_transactions].should == 1137
39
+ data_record[:report_serial_number ].should == 1
40
+ data_record[:currency_paid ].should == "USD"
41
+ end
42
+ end
@@ -0,0 +1,17 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe "the collection report file" do
4
+ it "should sort out the metadata from the file name" do
5
+ report = GlobalCollect::LogParsing::CollectionReport::ReportFile.new("555555550145.mt1")
6
+ report.account_id.should == "5555"
7
+ report.master_account_id.should == "5555"
8
+ report.date.should == Date.ordinal(2010, 145)
9
+ report.environment.should == :production
10
+ end
11
+
12
+ it "should parse" do
13
+ report = GlobalCollect::LogParsing::CollectionReport::ReportFile.new(support_path("555555550145.mt1"))
14
+ report.parse
15
+ report.data.should_not be_blank
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe "the financial statement report file" do
4
+ it "should sort out the metadata from the file name" do
5
+ report = GlobalCollect::LogParsing::FinancialStatement::ReportFile.new("FS55550148COMPANY.asc")
6
+ report.account_id.should == "5555"
7
+ report.date.should == Date.ordinal(2010, 148)
8
+ report.environment.should == :production
9
+ end
10
+
11
+ def check_report(report)
12
+ report.data.should_not be_blank
13
+ report.data[:header][:record_type].should == :file_header
14
+ report.data[:header][:relation_number].should == 'R0555500'
15
+ report.data[:header][:date_production].should == Date.ordinal(2010, 148)
16
+
17
+ report.data[:trailer][:record_type].should == :file_trailer
18
+ report.data[:trailer][:number_of_records].should == 12
19
+ report.data[:trailer][:relation_number].should == 'R0555500'
20
+ report.data[:trailer][:date_production].should == Date.ordinal(2010, 148)
21
+
22
+ report.data[:data].size.should == 10
23
+ report.data[:data].each{|d| d[:record_type].should == :data_record }
24
+ data_record = report.data[:data].first
25
+ data_record[:reference_number_week].should == "21FR"
26
+ data_record[:reference_number_year].should == 2010
27
+ data_record[:class ].should == :collection_reports
28
+ data_record[:account_id ].should == 5555
29
+ data_record[:description ].should == "25-05-2010 25-05-2010"
30
+ data_record[:currency ].should == "USD"
31
+ data_record[:amount ].should == 3333333
32
+ data_record[:amount_sign ].should == "+"
33
+ end
34
+
35
+ it "should parse with BOM" do
36
+ report = GlobalCollect::LogParsing::FinancialStatement::ReportFile.new(support_path("FS55550148COMPANY.asc"))
37
+ report.parse
38
+ check_report(report)
39
+ end
40
+
41
+ it "should parse without BOM" do
42
+ require 'tempfile'
43
+ tf = Tempfile.new("FS55550148COMPANY.asc")
44
+ tf.write(File.read(support_path("FS55550148COMPANY.asc"))[3..-1]) #remove BOM
45
+ tf.rewind
46
+ report = GlobalCollect::LogParsing::FinancialStatement::ReportFile.new(tf.path)
47
+ report.parse
48
+ check_report(report)
49
+ tf.close(true)
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe "the payment report parser" do
4
+ it "should parse" do
5
+ file = File.open(support_path("55550141.wr1"), "r")
6
+ data = GlobalCollect::LogParsing::PaymentReport::Parser.parse(file)
7
+
8
+ data[:header].size.should == 1
9
+ data[:header].first[:record_type].should == :file_header
10
+ data[:header].first[:filename].should == '55550141'
11
+
12
+ data[:batch_header].size.should == 1
13
+ data[:batch_header].first[:record_type].should == :batch_header
14
+ data[:batch_header].first[:merchant_id].should == 5555
15
+
16
+ data[:trailer].size.should == 1
17
+ data[:trailer].first[:record_type].should == :file_trailer
18
+ data[:trailer].first[:filename].should == '55550141'
19
+
20
+ data[:batch_trailer].size.should == 1
21
+ data[:batch_trailer].first[:record_type].should == :batch_trailer
22
+ data[:batch_trailer].first[:number_of_records].should == 443
23
+
24
+ data[:information_records].size.should == 1
25
+ data[:information_records].first[:total_amount_due].should == 211
26
+
27
+ data[:data_records].size.should == 2
28
+ data[:data_records].each do |r|
29
+ r[:record_type].should == :settlement
30
+ r[:record_category].should == :processed
31
+ r[:payment][:payment_method].should == "CC"
32
+ r[:order][:card_number].should == "************1234"
33
+ r[:refund].values.each {|v| v.should be_nil }
34
+ r[:chargeback].values.each{|v| v.should be_nil }
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe "the payment report file" do
4
+ it "should sort out the metadata from the file name (prod)" do
5
+ report = GlobalCollect::LogParsing::PaymentReport::ReportFile.new("55550141.wr1")
6
+ report.account_id.should == "5555"
7
+ report.date.should == Date.ordinal(2010, 141)
8
+ report.environment.should == :production
9
+ end
10
+
11
+ it "should sort out the metadata from the file name (test)" do
12
+ report = GlobalCollect::LogParsing::PaymentReport::ReportFile.new("55550141.wrt")
13
+ report.account_id.should == "5555"
14
+ report.date.should == Date.ordinal(2010, 141)
15
+ report.environment.should == :test
16
+ end
17
+
18
+ it "should parse" do
19
+ report = GlobalCollect::LogParsing::PaymentReport::ReportFile.new(support_path("55550141.wr1"))
20
+ report.parse
21
+ report.data.should_not be_blank
22
+ end
23
+ end