global_collect 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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