PS2Format 1.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 +7 -0
- data/lib/PS2Format/exception/InvalidAmountException.rb +8 -0
- data/lib/PS2Format/exception/InvalidNIBException.rb +8 -0
- data/lib/PS2Format/exception/InvalidOperationTypeException.rb +8 -0
- data/lib/PS2Format/exception/InvalidReferenceException.rb +8 -0
- data/lib/PS2Format/footer.rb +44 -0
- data/lib/PS2Format/header.rb +100 -0
- data/lib/PS2Format/operation.rb +122 -0
- data/lib/PS2Format/operation_type.rb +59 -0
- data/lib/PS2Format/ps2.rb +170 -0
- data/lib/PS2Format/record.rb +146 -0
- data/lib/PS2Format/version.rb +3 -0
- data/lib/locales/ps2.en.yml +24 -0
- data/lib/locales/ps2.pt-PT.yml +24 -0
- data/lib/ps2_format.rb +13 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5962014857f59b089dd934aec18c5945b46550fe
|
4
|
+
data.tar.gz: 1149120a9e49b39638af68e4cdafbce058210359
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8feb4232ac1c0b01999cee780662afe1d7d4592ebe9bcae8640bdfb9ac90924a5b99ab43ba86fc25116db02570b6f86715b5db0e365cb5b7d5aa5221aa9d1883
|
7
|
+
data.tar.gz: c87cddedd29933d204087b6f47fdadd01ddb31cd0c30e72f496b42743fda75e3a986f3ac606d6b12918f0ad99bfd963a46c6862df593b61f6d6ee3549b90317b
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PS2Format
|
2
|
+
class Footer < Record
|
3
|
+
FOOTER_RECORD_TYPE = '9'
|
4
|
+
|
5
|
+
def initialize(ps2)
|
6
|
+
@ps2 = ps2
|
7
|
+
super({format: FORMAT,
|
8
|
+
record_type: FOOTER_RECORD_TYPE,
|
9
|
+
operation_type: TRANSFER_OPCODE,
|
10
|
+
filler1: '00',
|
11
|
+
record_status: RECORD_STATUS,
|
12
|
+
filler2: ''.rjust(6, '0'),
|
13
|
+
num_operations: @ps2.operations.size.to_s.rjust(14, '0'),
|
14
|
+
total_amount: @ps2.total_amount.to_s.rjust(13, '0'),
|
15
|
+
filler3: ''.rjust(38, '0')})
|
16
|
+
process_options({operation_type: @ps2.operation_type})
|
17
|
+
end
|
18
|
+
|
19
|
+
def total_amount
|
20
|
+
metadata.total_amount.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def num_operations
|
24
|
+
metadata.num_operations.to_i
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate
|
28
|
+
add_error(:format, value: format) if format != FORMAT
|
29
|
+
add_error(:record_type, should_be: FOOTER_RECORD_TYPE, is: record_type) if record_type != FOOTER_RECORD_TYPE
|
30
|
+
add_error(:record_status, value: record_status) if record_status.size != 1
|
31
|
+
add_error :num_operations, num_operations: num_operations if num_operations != @ps2.operations.size
|
32
|
+
add_error :total_amount, total_amount: total_amount if total_amount != @ps2.total_amount
|
33
|
+
add_error :line_size, component: 'Footer', size: data.length if data.length != 80
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def process_options(opts)
|
39
|
+
operation_type = opts.delete(:operation_type)
|
40
|
+
|
41
|
+
self.operation_type = operation_type if operation_type
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'citizenship'
|
3
|
+
require 'PS2Format/exception/InvalidOperationTypeException'
|
4
|
+
require 'PS2Format/exception/InvalidNIBException'
|
5
|
+
|
6
|
+
module PS2Format
|
7
|
+
class Header < Record
|
8
|
+
HEADER_RECORD_TYPE = '1'
|
9
|
+
COIN = 'EUR'
|
10
|
+
FILLER_SIZE = 19
|
11
|
+
|
12
|
+
def initialize(arg = nil)
|
13
|
+
case arg
|
14
|
+
when Hash, NilClass
|
15
|
+
super({format: FORMAT,
|
16
|
+
record_type: HEADER_RECORD_TYPE,
|
17
|
+
operation_type: TRANSFER_OPCODE,
|
18
|
+
account_status: ACCOUNT_STATUS,
|
19
|
+
record_status: RECORD_STATUS,
|
20
|
+
ordering_nib: ''.rjust(21, '0'),
|
21
|
+
coin: COIN,
|
22
|
+
processing_date: Record.date_to_str(Date.today),
|
23
|
+
reference: ''.rjust(REFERENCE_FIELD_SIZE, '0'),
|
24
|
+
filler: ''.rjust(FILLER_SIZE, '0')}.merge(arg || {}))
|
25
|
+
process_options(arg || {})
|
26
|
+
when String
|
27
|
+
super({format: arg[0..2],
|
28
|
+
record_type: arg[3..3],
|
29
|
+
operation_type: arg[4..5],
|
30
|
+
account_status: arg[6..7],
|
31
|
+
record_status: arg[8..8],
|
32
|
+
ordering_nib: arg[9..29],
|
33
|
+
coin: arg[30..32],
|
34
|
+
processing_date: arg[33..40],
|
35
|
+
reference: arg[41..60],
|
36
|
+
filler: arg[61..79]})
|
37
|
+
else
|
38
|
+
raise ArgumentError, "String or Hash expected"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def reference=(ref)
|
43
|
+
metadata.reference = Record.remove_accents(String(ref)).rjust(REFERENCE_FIELD_SIZE, '0')
|
44
|
+
end
|
45
|
+
|
46
|
+
def reference
|
47
|
+
metadata.reference.lstrip
|
48
|
+
end
|
49
|
+
|
50
|
+
def ordering_nib=(ordering_nib)
|
51
|
+
metadata.ordering_nib = Record.pre_process_nib(ordering_nib)
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate
|
55
|
+
add_error(:format, value: format) if format != FORMAT
|
56
|
+
add_error(:record_type, should_be: HEADER_RECORD_TYPE, is: record_type) if record_type != HEADER_RECORD_TYPE
|
57
|
+
add_error(:record_status, value: record_status) if record_status.size != 1
|
58
|
+
add_error(:account_status, value: account_status) if account_status.size != 2
|
59
|
+
add_error(:coin, coin: coin) if coin != COIN
|
60
|
+
add_error(:date, date: processing_date) unless Record.valid_date?(processing_date)
|
61
|
+
add_error(:reference, size: reference.size) if reference.size != REFERENCE_FIELD_SIZE
|
62
|
+
add_error(:filler) if filler.size != 19 or filler.to_i != 0
|
63
|
+
add_error(:line_size, component: 'Header', size: data.length) if data.length != 80
|
64
|
+
|
65
|
+
begin
|
66
|
+
Citizenship.valid_nib!(ordering_nib)
|
67
|
+
rescue Citizenship::Error => e
|
68
|
+
add_error(:nib, error: e.message)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def process_options(opts)
|
75
|
+
|
76
|
+
if !opts.empty?
|
77
|
+
raise PS2Format::InvalidOperationTypeException.new I18n.t("ps2.exceptions.invalid_operation_type") if opts.delete(:operation_type).nil?
|
78
|
+
begin
|
79
|
+
Citizenship.valid_nib!(opts.delete(:ordering_nib))
|
80
|
+
ordering_nib = opts.delete(:ordering_nib)
|
81
|
+
rescue Citizenship::Error => e
|
82
|
+
raise PS2Format::InvalidNIBException.new e.message
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
ref = opts.delete(:reference)
|
87
|
+
operation_type = opts.delete(:operation_type)
|
88
|
+
date = opts.delete(:date)
|
89
|
+
account_status = opts.delete(:account_status)
|
90
|
+
record_status = opts.delete(:record_status)
|
91
|
+
|
92
|
+
self.ordering_nib = ordering_nib if ordering_nib
|
93
|
+
self.reference = ref if ref
|
94
|
+
self.operation_type = operation_type if operation_type
|
95
|
+
self.date = date if date
|
96
|
+
self.account_status = account_status if account_status
|
97
|
+
self.record_status = record_status if record_status
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'citizenship'
|
2
|
+
require 'PS2Format/exception/InvalidAmountException'
|
3
|
+
|
4
|
+
module PS2Format
|
5
|
+
class Operation < Record
|
6
|
+
OPERATION_RECORD_TYPE = '2'
|
7
|
+
|
8
|
+
TRANSFER_REFERENCE_FIELD_SIZE = 15
|
9
|
+
AMOUNT_FIELD_SIZE = 13
|
10
|
+
COMPANY_REFERENCE_FIELD_SIZE = 20
|
11
|
+
|
12
|
+
def initialize(arg = nil)
|
13
|
+
case arg
|
14
|
+
when Hash, NilClass
|
15
|
+
super({format: FORMAT,
|
16
|
+
record_type: OPERATION_RECORD_TYPE,
|
17
|
+
operation_type: TRANSFER_OPCODE,
|
18
|
+
account_status: ACCOUNT_STATUS,
|
19
|
+
record_status: RECORD_STATUS,
|
20
|
+
nib: ''.rjust(21, '0'),
|
21
|
+
amount: ''.rjust(AMOUNT_FIELD_SIZE, '0'),
|
22
|
+
company_reference: ''.rjust(COMPANY_REFERENCE_FIELD_SIZE, '0'),
|
23
|
+
transfer_reference: ''.rjust(TRANSFER_REFERENCE_FIELD_SIZE, '0'),
|
24
|
+
filler: '00'})
|
25
|
+
process_options(arg || {})
|
26
|
+
when String
|
27
|
+
super({format: arg[0..2],
|
28
|
+
record_type: arg[3..3],
|
29
|
+
operation_type: arg[4..5],
|
30
|
+
account_status: arg[6..7],
|
31
|
+
record_status: arg[8..8],
|
32
|
+
nib: arg[9..29],
|
33
|
+
amount: arg[30..42],
|
34
|
+
company_reference: arg[43..62],
|
35
|
+
transfer_reference: arg[63..77],
|
36
|
+
filler: arg[78..79]})
|
37
|
+
else
|
38
|
+
raise ArgumentError, "String or Hash expected"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def amount=(value)
|
43
|
+
metadata.amount = String(value).rjust(AMOUNT_FIELD_SIZE, '0')
|
44
|
+
end
|
45
|
+
|
46
|
+
def amount
|
47
|
+
metadata.amount.to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def transfer_reference=(ref)
|
51
|
+
metadata.transfer_reference = Record.remove_accents(String(ref)).rjust(TRANSFER_REFERENCE_FIELD_SIZE, ' ')
|
52
|
+
end
|
53
|
+
|
54
|
+
def transfer_reference
|
55
|
+
metadata.transfer_reference.lstrip
|
56
|
+
end
|
57
|
+
|
58
|
+
def company_reference=(ref)
|
59
|
+
metadata.company_reference = Record.remove_accents(String(ref)).rjust(COMPANY_REFERENCE_FIELD_SIZE, ' ')
|
60
|
+
end
|
61
|
+
|
62
|
+
def company_reference
|
63
|
+
metadata.company_reference.lstrip
|
64
|
+
end
|
65
|
+
|
66
|
+
def nib=(nib)
|
67
|
+
metadata.nib = Record.pre_process_nib(nib)
|
68
|
+
end
|
69
|
+
|
70
|
+
class << self
|
71
|
+
|
72
|
+
def validate_options(options)
|
73
|
+
raise PS2Format::InvalidOperationTypeException.new I18n.t("ps2.exceptions.invalid_operation_type") if options[:operation_type].nil?
|
74
|
+
raise InvalidAmountException.new I18n.t("ps2.exceptions.invalid_amount") if !(options[:amount].is_a? Numeric) or options[:amount].size > 11 or options[:amount] <= 0
|
75
|
+
raise InvalidReferenceException.new I18n.t("ps2.exceptions.invalid_reference") if options[:operation_type].to_i > 50 and options[:company_reference].nil?
|
76
|
+
|
77
|
+
begin
|
78
|
+
Citizenship.valid_nib!(options[:nib])
|
79
|
+
rescue Citizenship::Error => e
|
80
|
+
raise PS2Format::InvalidOperationTypeException.new e.message
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate(include_obj = false)
|
87
|
+
add_error(:format, value: format) if format != FORMAT
|
88
|
+
add_error(:record_type, should_be: OPERATION_RECORD_TYPE, is: record_type) if record_type != OPERATION_RECORD_TYPE
|
89
|
+
add_error(:record_status, value: record_status) if record_status.size != 1
|
90
|
+
add_error(:account_status, value: account_status) if account_status.size != 2
|
91
|
+
add_error(:amount, amount: amount) if !(amount.is_a? Numeric) or amount <= 0 or amount.size > 11
|
92
|
+
add_error(:line_size, component: 'Operation', size: data.length) if data.length != 80
|
93
|
+
|
94
|
+
begin
|
95
|
+
Citizenship.valid_nib!(nib)
|
96
|
+
rescue Citizenship::Error => e
|
97
|
+
add_error(:nib, error: e.message)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def process_options(opts)
|
104
|
+
|
105
|
+
nib = opts.delete(:nib)
|
106
|
+
amount = opts.delete(:amount)
|
107
|
+
transfer_reference = opts.delete(:transfer_reference)
|
108
|
+
company_reference = opts.delete(:company_reference)
|
109
|
+
operation_type = opts.delete(:operation_type)
|
110
|
+
account_status = opts.delete(:account_status)
|
111
|
+
record_status = opts.delete(:record_status)
|
112
|
+
|
113
|
+
self.nib = nib if nib
|
114
|
+
self.amount = amount if amount
|
115
|
+
self.transfer_reference = transfer_reference if transfer_reference
|
116
|
+
self.company_reference = company_reference if company_reference
|
117
|
+
self.operation_type = operation_type if operation_type
|
118
|
+
self.account_status = account_status if account_status
|
119
|
+
self.record_status = record_status if record_status
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
module PS2Format
|
3
|
+
class OperationType
|
4
|
+
|
5
|
+
@@expense = {}
|
6
|
+
@@expense[:water] = "01"
|
7
|
+
@@expense[:gas] = "02"
|
8
|
+
@@expense[:electricity] = "03"
|
9
|
+
@@expense[:gas_electricity] = "04"
|
10
|
+
@@expense[:cellphone] = "05"
|
11
|
+
@@expense[:telex] = "06"
|
12
|
+
@@expense[:house_rent] = "07"
|
13
|
+
@@expense[:payroll] = "08"
|
14
|
+
@@expense[:suppliers] = "09"
|
15
|
+
@@expense[:transfer] = "12"
|
16
|
+
|
17
|
+
@@income = {}
|
18
|
+
@@income[:water] = "51"
|
19
|
+
@@income[:gas] = "52"
|
20
|
+
@@income[:electricity] = "53"
|
21
|
+
@@income[:gas_electricity] = "54"
|
22
|
+
@@income[:cellphone] = "55"
|
23
|
+
@@income[:telex] = "56"
|
24
|
+
@@income[:house_rent] = "57"
|
25
|
+
@@income[:land_public_service] = "58"
|
26
|
+
@@income[:suppliers] = "59"
|
27
|
+
@@income[:insurance] = "60"
|
28
|
+
@@income[:quota] = "61"
|
29
|
+
@@income[:transfer] = "62"
|
30
|
+
@@income[:data_com_public_service] = "63"
|
31
|
+
@@income[:collect] = "64"
|
32
|
+
@@income[:rented_circuits] = "65"
|
33
|
+
@@income[:various_services] = "66"
|
34
|
+
@@income[:sanitation] = "67"
|
35
|
+
@@income[:water_electricity] = "68"
|
36
|
+
@@income[:water_sanitation] = "69"
|
37
|
+
@@income[:telecom_ctt] = "70"
|
38
|
+
@@income[:iva_refund_iva_charge] = "71"
|
39
|
+
@@income[:tv_license] = "72"
|
40
|
+
@@income[:TELEBIP] = "73"
|
41
|
+
@@income[:VIDEOTEX] = "74"
|
42
|
+
@@income[:digital_phone] = "75"
|
43
|
+
@@income[:green_number] = "76"
|
44
|
+
@@income[:phone_porto] = "77"
|
45
|
+
@@income[:green_number_IN] = "78"
|
46
|
+
@@income[:blue_number] = "79"
|
47
|
+
@@income[:rented_circuits_porto] = "80"
|
48
|
+
@@income[:CLIP] = "81"
|
49
|
+
|
50
|
+
def self.expense
|
51
|
+
@@expense
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.income
|
55
|
+
@@income
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module PS2Format
|
4
|
+
class BatchTransfer
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def_delegators :@header, :operation_type, :ordering_nib
|
8
|
+
attr_accessor :header, :operations
|
9
|
+
|
10
|
+
def initialize(opts = {})
|
11
|
+
@header = Header.new(opts)
|
12
|
+
@operations = []
|
13
|
+
process_options(opts)
|
14
|
+
end
|
15
|
+
|
16
|
+
def ordering_nib=(nib)
|
17
|
+
@header.ordering_nib = Record.pre_process_nib(nib)
|
18
|
+
end
|
19
|
+
|
20
|
+
def reference=(ref)
|
21
|
+
@header.reference = String(ref).rjust(Header::REFERENCE_FIELD_SIZE, ' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
def reference
|
25
|
+
@header.reference.lstrip
|
26
|
+
end
|
27
|
+
|
28
|
+
def processing_date
|
29
|
+
Record.str_to_date(@header.processing_date)
|
30
|
+
end
|
31
|
+
|
32
|
+
def processing_date=(date)
|
33
|
+
@header.processing_date = Record.date_to_str(date)
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_operation!(options = {})
|
37
|
+
Operation.validate_options(options)
|
38
|
+
@operations << Operation.new(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_operation(options = {})
|
42
|
+
oper = Operation.new(options)
|
43
|
+
@operations << oper
|
44
|
+
oper.validate
|
45
|
+
oper.errors
|
46
|
+
end
|
47
|
+
|
48
|
+
def total_amount
|
49
|
+
@operations.map(&:amount).reduce(0, &:+)
|
50
|
+
end
|
51
|
+
|
52
|
+
def income_amount
|
53
|
+
@operations.map { |op|
|
54
|
+
if op.operation_type.to_i >= 50 then op.amount.to_i else 0 end
|
55
|
+
}.reduce(0, &:+)
|
56
|
+
end
|
57
|
+
|
58
|
+
def expense_amount
|
59
|
+
@operations.map { |op|
|
60
|
+
if op.operation_type.to_i < 50 then op.amount.to_i else 0 end
|
61
|
+
}.reduce(0, &:+)
|
62
|
+
end
|
63
|
+
|
64
|
+
def footer
|
65
|
+
Footer.new(self)
|
66
|
+
end
|
67
|
+
|
68
|
+
def valid?
|
69
|
+
validate
|
70
|
+
errors.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
def invalid?
|
74
|
+
not(valid?)
|
75
|
+
end
|
76
|
+
|
77
|
+
def validate
|
78
|
+
oper_errors = @operations.each do |o|
|
79
|
+
o.validate
|
80
|
+
end
|
81
|
+
|
82
|
+
@header.validate
|
83
|
+
|
84
|
+
footer.validate
|
85
|
+
end
|
86
|
+
|
87
|
+
def data
|
88
|
+
to_a.map(&:to_s).join("\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
def errors
|
92
|
+
@header.errors + operation_errors + footer.errors
|
93
|
+
end
|
94
|
+
|
95
|
+
def structured_errors
|
96
|
+
{ header: @header.errors, operations: structured_operation_errors, footer: footer.errors }
|
97
|
+
end
|
98
|
+
|
99
|
+
def operation_errors
|
100
|
+
@operations.map(&:errors).reject(&:empty?)
|
101
|
+
end
|
102
|
+
|
103
|
+
def structured_operation_errors
|
104
|
+
error_hash = {}
|
105
|
+
@operations.each do |op|
|
106
|
+
error_hash[op] = op.errors unless op.errors.empty?
|
107
|
+
end
|
108
|
+
error_hash
|
109
|
+
end
|
110
|
+
|
111
|
+
def save(file_name)
|
112
|
+
stream = File.new(file_name, "w")
|
113
|
+
validate
|
114
|
+
if errors.empty?
|
115
|
+
marshall stream
|
116
|
+
stream.close
|
117
|
+
else
|
118
|
+
errors
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def marshall(stream)
|
123
|
+
stream << to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
data
|
128
|
+
end
|
129
|
+
|
130
|
+
class << self
|
131
|
+
def read(file_name)
|
132
|
+
stream = File.new(file_name,'r')
|
133
|
+
file = unmarshall stream
|
134
|
+
stream.close
|
135
|
+
file
|
136
|
+
end
|
137
|
+
|
138
|
+
def unmarshall(stream)
|
139
|
+
lines = stream.readlines
|
140
|
+
|
141
|
+
header = Header.new(lines[0])
|
142
|
+
|
143
|
+
operations = lines[1..-2].reduce([]) { |array, line| array << Operation.new(line) }
|
144
|
+
|
145
|
+
ps2 = PS2Format::BatchTransfer.new
|
146
|
+
ps2.header = header
|
147
|
+
ps2.operations = operations
|
148
|
+
#NOTICE: Footer is built on the fly! This should not happen for unmarshalling
|
149
|
+
|
150
|
+
ps2
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
def process_options(opts)
|
156
|
+
nib = opts.delete(:ordering_nib)
|
157
|
+
date = opts.delete(:processing_date)
|
158
|
+
ref = opts.delete(:reference)
|
159
|
+
|
160
|
+
self.ordering_nib = nib if nib
|
161
|
+
self.processing_date = date if date
|
162
|
+
self.reference = ref if ref
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_a
|
166
|
+
[@header.data, @operations.map(&:data), footer.data].flatten
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
#coding: utf-8
|
2
|
+
require 'ostruct'
|
3
|
+
require 'delegate'
|
4
|
+
require 'i18n'
|
5
|
+
|
6
|
+
module PS2Format
|
7
|
+
# Compoents (Header, Operation and Footer) are validatable
|
8
|
+
# and delegate method calls to an OpenStruct containing
|
9
|
+
# the component's metadata
|
10
|
+
#
|
11
|
+
class Record < SimpleDelegator
|
12
|
+
alias :metadata :__getobj__
|
13
|
+
|
14
|
+
FORMAT = 'ps2'
|
15
|
+
TRANSFER_OPCODE = '12'
|
16
|
+
ACCOUNT_STATUS = '00'
|
17
|
+
RECORD_STATUS = '0'
|
18
|
+
REFERENCE_FIELD_SIZE = 20
|
19
|
+
|
20
|
+
#The error list
|
21
|
+
attr_reader :errors
|
22
|
+
|
23
|
+
def initialize(metadata)
|
24
|
+
super(OpenStruct.new(metadata))
|
25
|
+
@errors = []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Validates the component
|
29
|
+
# Inheriting classes must implement #validate and use
|
30
|
+
# Componenet#add_error if errors found
|
31
|
+
def valid?
|
32
|
+
@errors.clear
|
33
|
+
validate
|
34
|
+
@errors.empty?
|
35
|
+
end
|
36
|
+
|
37
|
+
def invalid?
|
38
|
+
not(valid?)
|
39
|
+
end
|
40
|
+
|
41
|
+
def format
|
42
|
+
metadata.format
|
43
|
+
end
|
44
|
+
|
45
|
+
def ordering_nib
|
46
|
+
metadata.ordering_nib
|
47
|
+
end
|
48
|
+
|
49
|
+
def operation_type
|
50
|
+
metadata.operation_type
|
51
|
+
end
|
52
|
+
|
53
|
+
def operation_type=(operation_type)
|
54
|
+
metadata.operation_type = operation_type
|
55
|
+
end
|
56
|
+
|
57
|
+
def date=(date)
|
58
|
+
if date.is_a? Date
|
59
|
+
metadata.date = date_to_str(date)
|
60
|
+
end
|
61
|
+
|
62
|
+
if date.is_a? String
|
63
|
+
metadata.date = date
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def account_status
|
68
|
+
metadata.account_status
|
69
|
+
end
|
70
|
+
|
71
|
+
def account_status=(status)
|
72
|
+
metadata.account_status = status
|
73
|
+
end
|
74
|
+
|
75
|
+
def record_status
|
76
|
+
metadata.record_status
|
77
|
+
end
|
78
|
+
|
79
|
+
def record_status=(status)
|
80
|
+
metadata.record_status = status
|
81
|
+
end
|
82
|
+
|
83
|
+
#Raw component data
|
84
|
+
def data
|
85
|
+
metadata.marshal_dump.values.map(&:to_s).reduce('', :<<)
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
def add_error(key, values = {})
|
90
|
+
error(key, 'ps2.errors', values)
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_account_status_error(key, values = {})
|
94
|
+
error("status_#{key}", 'ps2.account_statuses', values)
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_status_error(key, values = {})
|
98
|
+
error("status_#{key}", 'ps2.statuses', values)
|
99
|
+
end
|
100
|
+
|
101
|
+
def error(key, scope, values = {})
|
102
|
+
@errors << I18n.t(key.to_sym, values.merge(scope: scope))
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.date_to_str(date)
|
106
|
+
date.strftime('%Y%m%d')
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.str_to_date(str)
|
110
|
+
Date.strptime(str, '%Y%m%d')
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.valid_date?(str)
|
114
|
+
str_to_date(str)
|
115
|
+
true
|
116
|
+
rescue ArgumentError
|
117
|
+
false
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.pre_process_nib(nib)
|
121
|
+
String(nib).delete(' ')
|
122
|
+
.delete('-')
|
123
|
+
.delete('.')
|
124
|
+
.rjust(21, '0')
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.remove_accents(text)
|
128
|
+
text = text.to_s
|
129
|
+
text = text.gsub(/[á|à|ã|â|ä]/, 'a').gsub(/(é|è|ê|ë)/, 'e').gsub(/(í|ì|î|ï)/, 'i').gsub(/(ó|ò|õ|ô|ö)/, 'o').gsub(/(ú|ù|û|ü)/, 'u')
|
130
|
+
text = text.gsub(/(Á|À|Ã|Â|Ä)/, 'A').gsub(/(É|È|Ê|Ë)/, 'E').gsub(/(Í|Ì|Î|Ï)/, 'I').gsub(/(Ó|Ò|Õ|Ô|Ö)/, 'O').gsub(/(Ú|Ù|Û|Ü)/, 'U')
|
131
|
+
text = text.gsub(/ñ/, 'n').gsub(/Ñ/, 'N')
|
132
|
+
text = text.gsub(/ç/, 'c').gsub(/Ç/, 'C')
|
133
|
+
text
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def ensure_options(options)
|
139
|
+
|
140
|
+
raise PS2Format::InvalidOperationTypeException.new I18n.t("ps2.exceptions.invalid_operation_type") if operation_type.nil?
|
141
|
+
raise PS2Format::InvalidNIBException.new I18n.t("ps2.exceptions.invalid_nib") if ordering_nib.nil? or ordering_nib.size != 19
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
en:
|
3
|
+
ps2:
|
4
|
+
errors:
|
5
|
+
format: "format should be 'PS2' and is %{value}"
|
6
|
+
nib: "Invalid NIB: %{error}"
|
7
|
+
record_type: "record_type should be %{should_be} and is %{is}"
|
8
|
+
operation_type: "operation type is inconsistent: %{value}"
|
9
|
+
account_status: "unexpected account_status: %{value}"
|
10
|
+
record_status: "unexpected record_status: %{value}"
|
11
|
+
coin: "coin must be 'EUR' but is %{coin}"
|
12
|
+
date: "invalid date: %{value}"
|
13
|
+
reference: "reference size show be 10 but is %{size}"
|
14
|
+
filler: "filler is misplaced or with wrong size"
|
15
|
+
line_size: "%{component} header data should be 80 bytes long but is: %{size}"
|
16
|
+
amount: "invalid amount: %{amount}"
|
17
|
+
num_operations: "unexpected number of operations: #{num_operations}"
|
18
|
+
total_amount: "unexpected total amount: #{total_amount}"
|
19
|
+
|
20
|
+
exceptions:
|
21
|
+
invalid_operation_type: "Invalid operation type"
|
22
|
+
invalid_nib: "Invalid NIB"
|
23
|
+
invalid_amount: "Invalid amount"
|
24
|
+
invalid_reference: "Invalid company reference"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#coding: utf-8
|
2
|
+
"pt-PT":
|
3
|
+
ps2:
|
4
|
+
errors:
|
5
|
+
format: "Formato devia ser 'PS2' mas é %{value}"
|
6
|
+
nib: "NIB Inválido: %{error}"
|
7
|
+
record_type: "Tipo de registo devias ser %{should_be} mas é %{is}"
|
8
|
+
operation_type: "Tipo de operação não suportada: %{value}"
|
9
|
+
account_status: "Erro na sutação da conta: %{value}"
|
10
|
+
record_status: "Erro na sutação do registo: %{value}"
|
11
|
+
coin: "moeda deve ser 'EUR' mas é %{coin}"
|
12
|
+
date: "Data de operação inválida: %{value}"
|
13
|
+
reference: "O tamanha da referência do ordenantes devia ter 10 bytes mas tem %{size}"
|
14
|
+
filler: "Filler is misplaced or with wrong size"
|
15
|
+
line_size: "%{component} devia ter 80 bytes mas tem: %{size}"
|
16
|
+
amount: "Importância inválida: %{amount}"
|
17
|
+
num_operations: "Número de operações inesperado: #{num_operations}"
|
18
|
+
total_amount: "Importância total inválida: #{total_amount}"
|
19
|
+
|
20
|
+
exceptions:
|
21
|
+
invalid_operation_type: "Tipo de operação inválida"
|
22
|
+
invalid_nib: "NIB inválido"
|
23
|
+
invalid_amount: "Montante introduziodo inválido"
|
24
|
+
invalid_reference: "Referência da empresa inválida"
|
data/lib/ps2_format.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "PS2Format/version"
|
2
|
+
require "PS2Format/ps2"
|
3
|
+
require "PS2Format/record"
|
4
|
+
require "PS2Format/header"
|
5
|
+
require "PS2Format/operation"
|
6
|
+
require "PS2Format/operation_type"
|
7
|
+
require "PS2Format/footer"
|
8
|
+
require "PS2Format/exception/InvalidOperationTypeException"
|
9
|
+
require "PS2Format/exception/InvalidNIBException"
|
10
|
+
require "PS2Format/exception/InvalidAmountException"
|
11
|
+
require "PS2Format/exception/InvalidReferenceException"
|
12
|
+
|
13
|
+
I18n.load_path += Dir.glob( File.join(File.dirname(__FILE__), 'locales'.freeze, '*.yml'.freeze) )
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: PS2Format
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Runtime Revolution
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-07-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: i18n
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.7.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.7.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: citizenship
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: test-unit
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 3.1.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 3.1.2
|
83
|
+
description: PS2 provides you the ability of creating PS2 type files used to make
|
84
|
+
financial transfers with a bank.
|
85
|
+
email: info@runtime-revolution.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/PS2Format/exception/InvalidAmountException.rb
|
91
|
+
- lib/PS2Format/exception/InvalidNIBException.rb
|
92
|
+
- lib/PS2Format/exception/InvalidOperationTypeException.rb
|
93
|
+
- lib/PS2Format/exception/InvalidReferenceException.rb
|
94
|
+
- lib/PS2Format/footer.rb
|
95
|
+
- lib/PS2Format/header.rb
|
96
|
+
- lib/PS2Format/operation.rb
|
97
|
+
- lib/PS2Format/operation_type.rb
|
98
|
+
- lib/PS2Format/ps2.rb
|
99
|
+
- lib/PS2Format/record.rb
|
100
|
+
- lib/PS2Format/version.rb
|
101
|
+
- lib/locales/ps2.en.yml
|
102
|
+
- lib/locales/ps2.pt-PT.yml
|
103
|
+
- lib/ps2_format.rb
|
104
|
+
homepage: https://github.com/runtimerevolution/ps2
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata: {}
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.4.6
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: PS2 provides you the ability of creating PS2 type files used to make financial
|
128
|
+
transfers with a bank.
|
129
|
+
test_files: []
|