ruby-hotfile 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9ada4084e4841bb645fec91e314bc2abb8739f2d71da8ed07ea78ce7f4bd540f
4
+ data.tar.gz: dfd9412ca7eeacc2aed6a67023f880f0df49d4c61486511073584a48c73ef06b
5
+ SHA512:
6
+ metadata.gz: 160bc7ab8ec531549bb156784ad5e9c3f362dcc1b29ebaa9017808911c10eae3d25bfdab3b6864c709efe8b19d1f63c03f43ee31152b0c12925c08aa37ea93f5
7
+ data.tar.gz: 3627b9d28611ce72d127f453db1aa1f098e90e94e031e9b691df7dfceaecd3ebbd324a899ee214558e7bc820792ad5fa63cf5decd30bb75fe176cb77ca8592c4
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ # Parses a string date into a Date object
5
+ class Date
6
+ require 'date'
7
+
8
+ def initialize(date_string)
9
+ @raw_value = date_string.to_s.strip
10
+ end
11
+
12
+ def to_date
13
+ return nil if @raw_value.delete('0') == ''
14
+
15
+ case @raw_value.length
16
+ when 0 then nil
17
+ when 5 then ::Date.strptime(@raw_value, '%d%b')
18
+ when 6 then ::Date.strptime(@raw_value, '%y%m%d')
19
+ when 7 then ::Date.strptime(@raw_value, '%d%b%y')
20
+ when 8 then ::Date.strptime(@raw_value, '%Y%m%d')
21
+ else raise NotImplementedError("Unknown date format: #{@raw_value}")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ # Parses a string integer (possibly overpunched) into a real Integer
5
+ class Integer
6
+ def initialize(int_string)
7
+ @raw_value = +int_string
8
+ end
9
+
10
+ def to_i
11
+ temp_string = @raw_value.clone
12
+ overpunch_char = temp_string[-1]
13
+ last_digit = case overpunch_char
14
+ when '{', '}' then 0
15
+ when /[0-9]/ then overpunch_char.to_i
16
+ when /[A-I]/ then overpunch_char.unpack1('c') - 64
17
+ when /[J-R]/ then overpunch_char.unpack1('c') - 73
18
+ end
19
+ temp_string[-1] = last_digit.to_s
20
+ temp_string = "-#{temp_string}" if negative?
21
+ temp_string.to_i
22
+ end
23
+
24
+ def negative?
25
+ '}JKLMNOPQR'.include? @raw_value[-1]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ ## Functions for parsing HOT file data
5
+ module Parser
6
+ def parse
7
+ return if @file.nil?
8
+
9
+ records = @file.lines(chomp: true).map { |line| parse_line line }
10
+ @parse ||= {
11
+ records: records,
12
+ date: records.map { |x| x[:date] }.compact.first,
13
+ doc_nr: records.map { |x| x[:doc_nr] }.compact.first,
14
+ transactions: records.map { |x| x[:transaction] }.compact.uniq.count
15
+ }
16
+ end
17
+
18
+ def unparsable
19
+ parse[:records].select! { |x| x[:data].nil? }
20
+ end
21
+
22
+ def parse_line(line)
23
+ code1, line_number, code2, payload = line.scan(/([A-Z]{3})(\d{8})(\d{2})(.*)/).flatten
24
+ code = code1 + code2
25
+ line_number = line_number.to_i
26
+
27
+ if (10..81).include? code2.to_i
28
+ date, transaction, doc_nr, check_digit, payload =
29
+ payload.scan(/
30
+ (\d{6})
31
+ (\d{6})
32
+ ([A-Z0-9 ]{14})
33
+ (\d)
34
+ (.*)
35
+ /x).flatten
36
+ end
37
+
38
+ begin
39
+ record_parser = Hotfile::Record.const_get(code)
40
+ rescue NameError
41
+ # Record type does not have a class implementing it.
42
+ end
43
+ data = record_parser.new(payload).parse if record_parser
44
+
45
+ {
46
+ code: { id: code, category: code1, number: code2.to_i },
47
+ line_number: line_number,
48
+ raw_payload: payload.strip,
49
+ date: Hotfile::Date.new(date).to_date,
50
+ transaction: transaction&.to_i,
51
+ doc_nr: doc_nr&.strip,
52
+ check_digit: check_digit&.to_i,
53
+ data: data
54
+ }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Document Amounts Record
6
+ class BAR64 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ fare, ti_indicator, equivalent_fare, total, system_id, fcm_indicator, booking_agent, outlet_type, fcp_indicator,
11
+ issuing_agent, reserved =
12
+ line.scan(/
13
+ (.{12})
14
+ (.)
15
+ (.{12})
16
+ (.{12})
17
+ ([A-Z0-9 ]{4})
18
+ ([A-Z0-9 ])
19
+ ([A-Z0-9 ]{6})
20
+ ([A-Z0-9 ])
21
+ ([A-Z0-9 ])
22
+ ([A-Z0-9 ]{8})
23
+ (.{38})
24
+ /x).flatten
25
+
26
+ @data = {
27
+ fare: fare.strip,
28
+ equivalent_fare: equivalent_fare.strip,
29
+ total_fare: total.strip,
30
+ system_id: system_id.strip,
31
+ booking_agent: booking_agent.strip,
32
+ outlet_type: outlet_type.strip,
33
+ issuing_agent: issuing_agent.strip,
34
+ indicators: {
35
+ ti: ti_indicator.strip,
36
+ fcm: fcm_indicator.strip,
37
+ fcp: fcp_indicator.strip
38
+ },
39
+ reserved: reserved.strip
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Additional Information–Passenger Record
6
+ class BAR65 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ name, passenger_data, date_of_birth, type, reserved =
11
+ line.scan(/
12
+ (.{49})
13
+ (.{29})
14
+ ([A-Z0-9 ]{7})
15
+ ([A-Z0-9 ]{3})
16
+ (.{8})
17
+ /x).flatten
18
+
19
+ @data = {
20
+ name: name.strip,
21
+ passenger_data: passenger_data.strip,
22
+ date_of_birth: Hotfile::Date.new(date_of_birth).to_date,
23
+ type: type.strip,
24
+ reserved: reserved.strip
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Additional Information–Form of Payment Record
6
+ class BAR66 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ sequence_number, payment_information, reserved =
11
+ line.scan(/
12
+ (\d)
13
+ (.{50})
14
+ (.{45})
15
+ /x).flatten
16
+
17
+ @data = {
18
+ sequence_number: sequence_number.to_i,
19
+ payment_information: payment_information.strip,
20
+ reserved: reserved.strip
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Additional Information–Taxes
6
+ # ! not tested, I hope this code works at all...
7
+ class BAR67 < Record
8
+ def initialize(line)
9
+ super
10
+
11
+ sequence_number, info_id, additional_info, reserved =
12
+ line.scan(/
13
+ (\d{2})
14
+ (.{4})
15
+ (.{70})
16
+ (.{20})
17
+ /x).flatten
18
+
19
+ @data = {
20
+ sequence_number: sequence_number.to_i,
21
+ info_id: info_id.strip,
22
+ additional_info: additional_info.strip,
23
+ reserved: reserved.strip
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Additional Card Information Record
6
+ # ! not tested, I hope this code works at all...
7
+ class BCC82 < Record
8
+ def initialize(line)
9
+ super
10
+
11
+ date_of_issue, transaction_number, payment_type, transaction_id, reserved =
12
+ line.scan(/
13
+ (\d{6})
14
+ (\d{6})
15
+ (.{10})
16
+ (.{25})
17
+ (.{76})
18
+ /x).flatten
19
+
20
+ @data = {
21
+ date_of_issue: Hotfile::Date.new(date_of_issue).to_date,
22
+ transaction_number: transaction_number.to_i,
23
+ transaction_id: transaction_id.strip,
24
+ payment_type: payment_type.strip,
25
+ reserved: reserved.strip
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Billing Analysis (Cycle) Header Record
6
+ class BCH02 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ processing_date, cycle, billing_end_date, run_id, reporting_end_date, reserved =
11
+ line.scan(/
12
+ ([A-Z0-9]{3})
13
+ (\d)
14
+ (\d{6})
15
+ ([A-Z0-9])
16
+ (\d{6})
17
+ (.{106})
18
+ /x).flatten
19
+
20
+ @data = {
21
+ processing_date: processing_date,
22
+ cycle: cycle.to_i,
23
+ billing_end_date: Hotfile::Date.new(billing_end_date).to_date,
24
+ run: run_id,
25
+ reporting_end_date: Hotfile::Date.new(reporting_end_date).to_date,
26
+ reserved: reserved.strip
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Billing Analysis (Cycle) Totals per Currency Type Record
6
+ class BCT95 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ processing_date_id, cycle_id, office_count, gross, remittance, commission, tax, commission_tax, reserved,
11
+ currency =
12
+ line.scan(/
13
+ ([A-Z0-9 ]{3})
14
+ (\d)
15
+ (\d{5})
16
+ (\d{14}.)
17
+ (\d{14}.)
18
+ (\d{14}.)
19
+ (\d{14}.)
20
+ (\d{14}.)
21
+ (.{35})
22
+ ([A-Z0-9]{4})
23
+ /x).flatten
24
+
25
+ @data = {
26
+ currency: currency.strip,
27
+ processing: { date: processing_date_id.strip, cycle: cycle_id.to_i },
28
+ office_count: Hotfile::Integer.new(office_count).to_i,
29
+ gross: Hotfile::Integer.new(gross).to_i,
30
+ remittance: Hotfile::Integer.new(remittance).to_i,
31
+ commission: Hotfile::Integer.new(commission).to_i,
32
+ tax: Hotfile::Integer.new(tax).to_i,
33
+ commission_tax: Hotfile::Integer.new(commission_tax).to_i,
34
+ reserved: reserved.strip
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## 3DS Authentication and Additional Card Payment Information Record
6
+ # ! not tested, I hope this code works at all...
7
+ class BCX83 < Record
8
+ def initialize(line)
9
+ super
10
+
11
+ date_of_issue, transaction_number, payment_type, auth_sequence_nr, reserved =
12
+ line.scan(/
13
+ (\d{6})
14
+ (\d{6})
15
+ (.{10})
16
+ (\d{2})
17
+ (.{99})
18
+ /x).flatten
19
+
20
+ @data = {
21
+ date_of_issue: Hotfile::Date.new(date_of_issue).to_date,
22
+ transaction_number: transaction_number.to_i,
23
+ auth_sequence_nr: auth_sequence_nr.strip,
24
+ payment_type: payment_type.strip,
25
+ reserved: reserved.strip
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## File Header Record
6
+ class BFH01 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ bsp_id, airline_code, revision, environment, processing_date, processing_time, country, file_sequence,
11
+ reserved =
12
+ line.scan(/
13
+ ([A-Z0-9]{3})
14
+ ([A-Z0-9]{3})
15
+ (\d{3})
16
+ ([A-Z0-9]{4})
17
+ (\d{6})
18
+ (\d{4})
19
+ ([A-Z]{2})
20
+ (\d{6})
21
+ (.{92})
22
+ /x).flatten
23
+
24
+ @data = {
25
+ bsp_id: bsp_id,
26
+ airline_code: airline_code,
27
+ revision: revision.to_i,
28
+ environment: environment,
29
+ processed: DateTime.parse("#{Hotfile::Date.new(processing_date).to_date} #{processing_time.insert(2, ':')}"),
30
+ country: country,
31
+ file_sequence: file_sequence.to_i,
32
+ reserved: reserved.strip
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## File Totals per Currency Type Record
6
+ class BFT99 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ bsp_id, office_count, gross, remittance, commission, tax, commission_tax, reserved, currency =
11
+ line.scan(/
12
+ ([A-Z0-9 ]{3})
13
+ (\d{5})
14
+ (\d{14}.)
15
+ (\d{14}.)
16
+ (\d{14}.)
17
+ (\d{14}.)
18
+ (\d{14}.)
19
+ (.{36})
20
+ ([A-Z0-9]{4})
21
+ /x).flatten
22
+
23
+ @data = {
24
+ currency: currency.strip,
25
+ bsp_id: bsp_id.strip,
26
+ office_count: Hotfile::Integer.new(office_count).to_i,
27
+ gross: Hotfile::Integer.new(gross).to_i,
28
+ remittance: Hotfile::Integer.new(remittance).to_i,
29
+ commission: Hotfile::Integer.new(commission).to_i,
30
+ tax: Hotfile::Integer.new(tax).to_i,
31
+ commission_tax: Hotfile::Integer.new(commission_tax).to_i,
32
+ reserved: reserved.strip
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Fare Calculation Record
6
+ class BKF81 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ sequence_number, info, reserved =
11
+ line.scan(/
12
+ (\d)
13
+ (.{87})
14
+ (.{8})
15
+ /x).flatten
16
+
17
+ @data = {
18
+ sequence_number: sequence_number.to_i,
19
+ info: info.strip,
20
+ reserved: reserved.strip
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Unticketed Point Information Record
6
+ # ! not tested, I hope this code works at all...
7
+ class BKI61 < Record
8
+ def initialize(line)
9
+ super
10
+
11
+ segment, arrival_airport, arrival_date, arrival_time, departure_date, departure_time, equipment, reserved =
12
+ line.scan(/
13
+ (\d)
14
+ ([A-Z0-9 ]{5})
15
+ ([A-Z0-9 ]{7})
16
+ ([\d ]{5})
17
+ ([A-Z0-9 ]{7})
18
+ ([\d ]{5})
19
+ ([A-Z0-9 ]{3})
20
+ (.{63})
21
+ /x).flatten
22
+
23
+ @data = {
24
+ segment: segment.to_i,
25
+ airport: arrival_airport.strip,
26
+ departure_datetime:
27
+ DateTime.parse("#{Hotfile::Date.new(departure_date).to_date} #{departure_time.strip.insert(2, ':')}"),
28
+ arrival_datetime:
29
+ DateTime.parse("#{Hotfile::Date.new(arrival_date).to_date} #{arrival_time.strip.insert(2, ':')}"),
30
+ equipment: equipment.strip,
31
+ reserved: reserved.strip
32
+ }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Unticketed Point Information Record
6
+ class BKI62 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ segment, departure_airport, departure_date, departure_time, departure_terminal, arrival_airport, arrival_date,
11
+ arrival_time, arrival_terminal, reserved =
12
+ line.scan(/
13
+ (\d)
14
+ ([A-Z0-9 ]{5})
15
+ ([A-Z0-9 ]{7})
16
+ ([\d ]{5})
17
+ ([A-Z0-9 ]{5})
18
+ ([A-Z0-9 ]{5})
19
+ ([A-Z0-9 ]{7})
20
+ ([\d ]{5})
21
+ ([A-Z0-9 ]{5})
22
+ (.{51})
23
+ /x).flatten
24
+
25
+ @data = {
26
+ segment: segment.to_i,
27
+ departure: {
28
+ airport: departure_airport.strip,
29
+ datetime:
30
+ DateTime.parse("#{Hotfile::Date.new(departure_date).to_date} #{departure_time.strip.insert(2, ':')}"),
31
+ terminal: departure_terminal.strip
32
+ },
33
+ arrival: {
34
+ airport: arrival_airport.strip,
35
+ datetime:
36
+ DateTime.parse("#{Hotfile::Date.new(arrival_date).to_date} #{arrival_time.strip.insert(2, ':')}"),
37
+ terminal: arrival_terminal.strip
38
+ },
39
+ reserved: reserved.strip
40
+ }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Itinerary Data Segment Record
6
+ class BKI63 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ segment, stopover, not_valid_before, not_valid_after, departure_airport, arrival_airport, carrier, cabin,
11
+ flight_number, booking_designator, departure_date, departure_time, booking_status, baggage, fare_basis,
12
+ ff_ref, fare_class, change_of_gauge, equipment, reserved =
13
+ line.scan(/
14
+ (\d)
15
+ ([A-Z])
16
+ ([A-Z0-9 ]{5})
17
+ ([A-Z0-9 ]{5})
18
+ ([A-Z0-9 ]{5})
19
+ ([A-Z0-9 ]{5})
20
+ ([A-Z0-9 ]{3})
21
+ ([A-Z0-9 ])
22
+ ([A-Z0-9 ]{5})
23
+ ([A-Z ]{2})
24
+ ([A-Z0-9 ]{7})
25
+ ([A-Z0-9 ]{5})
26
+ ([A-Z ]{2})
27
+ ([A-Z0-9 ]{3})
28
+ ([A-Z0-9 ]{15})
29
+ ([A-Z0-9 ]{20})
30
+ ([A-Z0-9 ]{3})
31
+ ([A-Z0-9 ])
32
+ ([A-Z0-9 ]{3})
33
+ (.{4})
34
+ /x).flatten
35
+
36
+ @data = {
37
+ segment: segment.to_i,
38
+ carrier: carrier.strip,
39
+ flight_number: flight_number.strip,
40
+ departure: {
41
+ airport: departure_airport.strip,
42
+ datetime:
43
+ DateTime.parse("#{Hotfile::Date.new(departure_date).to_date} #{departure_time.strip.insert(2, ':')}")
44
+ },
45
+ stopover: stopover.strip,
46
+ arrival: {
47
+ airport: arrival_airport.strip
48
+ },
49
+ validity: {
50
+ not_before: Hotfile::Date.new(not_valid_before).to_date,
51
+ not_after: Hotfile::Date.new(not_valid_after).to_date
52
+ },
53
+ cabin: cabin.strip,
54
+ baggage: baggage.strip,
55
+ equipment: equipment.strip,
56
+ booking: {
57
+ designator: booking_designator.strip,
58
+ status: booking_status.strip,
59
+ frequent_flyer_ref: ff_ref.strip,
60
+ fare_basis: fare_basis.strip,
61
+ fare_class: fare_class.strip,
62
+ change_of_gauge: change_of_gauge.strip
63
+ },
64
+ reserved: reserved.strip
65
+ }
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hotfile
4
+ class Record
5
+ ## Form of Payment Record
6
+ class BKP84 < Record
7
+ def initialize(line)
8
+ super
9
+
10
+ date_of_issue, transaction_number, payment_type, amount, account, expiry, extend_code, approval_code,
11
+ invoice_number, invoice_date, remittance_amount, verification, reserved, currency =
12
+ line.scan(/
13
+ (\d{6})
14
+ (\d{6})
15
+ ([A-Z0-9 ]{10})
16
+ (\d{10}.)
17
+ ([A-Z0-9 ]{19})
18
+ ([A-Z0-9 ]{4})
19
+ ([A-Z0-9 ]{2})
20
+ ([A-Z0-9 ]{6})
21
+ ([A-Z0-9 ]{14})
22
+ ([\d ]{6})
23
+ (\d{10}.)
24
+ (.)
25
+ (.{23})
26
+ ([A-Z0-9]{4})
27
+ /x).flatten
28
+
29
+ @data = {
30
+ currency: currency.strip,
31
+ date_of_issue: Hotfile::Date.new(date_of_issue).to_date,
32
+ transaction_number: Hotfile::Integer.new(transaction_number).to_i,
33
+ payment_type: payment_type.strip,
34
+ amount: Hotfile::Integer.new(amount).to_i,
35
+ account: account.strip,
36
+ expiry: expiry.strip,
37
+ extend_code: extend_code.strip,
38
+ approval_code: approval_code.strip,
39
+ invoice_number: invoice_number.strip,
40
+ invoice_date: Hotfile::Date.new(invoice_date).to_date,
41
+ remittance_amount: Hotfile::Integer.new(remittance_amount).to_i,
42
+ verification: verification.strip,
43
+ reserved: reserved.strip
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end