sepa_king 0.0.4 → 0.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d39443f2a8e1403804bfc6dd0eeb03f9e5e9d845
4
- data.tar.gz: 7fa5f5072912dee37819412cc0e05229c371b33a
3
+ metadata.gz: 5cc20b05ec4228c5ed453503dd90329e3b6366b3
4
+ data.tar.gz: 884d0f00387ef8d8a50b06a529a92d4220897773
5
5
  SHA512:
6
- metadata.gz: a6aa54767870360c955f350ae74bd3777fc6f7928b9cf9b75806689e76f4b70db70f5f722e426c7b4df075b6dcba939e560e84af479d56417c2b8caac9b173a1
7
- data.tar.gz: 9a06003c4a9d9c9fdbf58d1d6430651888339317c1f1a8f98d2f5c1a5f320b7b60457e3fae7e7ce8d8c74e6fdc9e3d4b2052aeae96e742079c3dbc4650a5cf1b
6
+ metadata.gz: 21b8f10e50d79e84d5a9ef67a54b15999e48efa0d13e8bc9df75387638395cc852e37c2e7f8541763aaee9fd74eee3e969d95a08a04a744e3ad16869ac326eaf
7
+ data.tar.gz: ae7a218d84ba1ee072079a1720d9da747d8c6d6c24179db3ec4b7cc7d1b7e3ac61556a9260836d36eeabf33111efd97910a210709f7d75ca6a17b12a45e28dc9
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,38 @@
1
+ We love pull requests. Here's a quick guide:
2
+
3
+ 1. Fork the repo.
4
+
5
+ 2. Run the tests. We only take pull requests with passing tests, and it's great
6
+ to know that you have a clean slate: `bundle && rake`
7
+
8
+ 3. Add a test for your change. Only refactoring and documentation changes
9
+ require no new tests. If you are adding functionality or fixing a bug, we need
10
+ a test!
11
+
12
+ 4. Make the test pass.
13
+
14
+ 5. Push to your fork and submit a pull request.
15
+
16
+
17
+ At this point you're waiting on us. We like to at least comment on, if not
18
+ accept, pull requests within three business days (and, typically, one business
19
+ day). We may suggest some changes or improvements or alternatives.
20
+
21
+ Some things that will increase the chance that your pull request is accepted,
22
+ taken straight from the Ruby on Rails guide:
23
+
24
+ * Use Rails idioms and helpers
25
+ * Include tests that fail without your code, and pass with it
26
+ * Update the documentation, the surrounding one, examples elsewhere, guides,
27
+ whatever is affected by your contribution
28
+
29
+ Syntax:
30
+
31
+ * Two spaces, no tabs.
32
+ * No trailing whitespace. Blank lines should not have any space.
33
+ * Prefer &&/|| over and/or.
34
+ * MyClass.my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
35
+ * a = b and not a=b.
36
+ * Follow the conventions you see used in the source already.
37
+
38
+ And in case we didn't emphasize it enough: we love tests!
@@ -1,7 +1,7 @@
1
- The MIT License (MIT)
2
-
3
1
  Copyright (c) 2013 Georg Leciejewski (Sales King GmbH) & Georg Ledermann
4
2
 
3
+ MIT License
4
+
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
7
7
  "Software"), to deal in the Software without restriction, including
data/README.md ADDED
@@ -0,0 +1,174 @@
1
+ # Ruby gem for creating SEPA XML files
2
+
3
+ [![Build Status](https://secure.travis-ci.org/salesking/sepa_king.png)](http://travis-ci.org/salesking/sepa_king)
4
+ [![Code Climate](https://codeclimate.com/github/salesking/sepa_king.png)](https://codeclimate.com/github/salesking/sepa_king)
5
+ [![Coverage Status](https://coveralls.io/repos/salesking/sepa_king/badge.png)](https://coveralls.io/r/salesking/sepa_king)
6
+
7
+ We love building payment applications! So after developing the [DTAUS library for Ruby](https://github.com/salesking/king_dtaus) we move on with SEPA.
8
+
9
+
10
+ ## Features
11
+
12
+ * Credit transfer initiation (pain.001.002.03)
13
+ * Debit transfer initiation (pain.008.002.02)
14
+ * Tested with Ruby 1.9.3 and 2.0.0
15
+
16
+
17
+ ## Installation
18
+
19
+ gem install sepa_king
20
+
21
+
22
+ ## Usage
23
+
24
+ How to create the XML for **Direct Debit Initiation** (in German: "Lastschriften")
25
+
26
+ ```ruby
27
+ # First: Create the main object
28
+ sdd = SEPA::DirectDebit.new(
29
+ # Name of the initiating party and creditor, in German: "Auftraggeber"
30
+ # String, max. 70 char
31
+ name: 'Gläubiger GmbH',
32
+
33
+ # Business Identifier Code (SWIFT-Code) of the creditor
34
+ # String, 8 or 11 char
35
+ bic: 'BANKDEFFXXX',
36
+
37
+ # International Bank Account Number of the creditor
38
+ # String, max. 34 chars
39
+ iban: 'DE87200500001234567890',
40
+
41
+ # Creditor Identifier, in German: Gläubiger-Identifikationsnummer
42
+ # String, max. 35 chars
43
+ creditor_identifier: 'DE98ZZZ09999999999'
44
+ )
45
+
46
+ # Second: Add transactions
47
+ sdd.add_transaction(
48
+ # Name of the debitor, in German: "Zahlungspflichtiger"
49
+ # String, max. 70 char
50
+ name: 'Zahlemann & Söhne GbR',
51
+
52
+ # Business Identifier Code (SWIFT-Code) of the debitor's account
53
+ # String, 8 or 11 char
54
+ bic: 'SPUEDE2UXXX',
55
+
56
+ # International Bank Account Number of the debitor's account
57
+ # String, max. 34 chars
58
+ iban: 'DE21500500009876543210',
59
+
60
+ # Amount in EUR
61
+ # Number with two decimal digit
62
+ amount: 39.99,
63
+
64
+ # OPTIONAL: End-To-End-Identification, will be submitted to the debitor
65
+ # String, max. 35 char
66
+ reference: 'XYZ/2013-08-ABO/6789',
67
+
68
+ # OPTIONAL: Unstructured remittance Information, in German "Verwendungszweck"
69
+ # String, max. 140 char
70
+ remittance_information: 'Vielen Dank für Ihren Einkauf!',
71
+
72
+ # Mandate identifikation, in German "Mandatsreferenz"
73
+ # String, max. 35 char
74
+ mandate_id: 'K-02-2011-12345',
75
+
76
+ # Mandate Date of signature, in German "Datum, zu dem das Mandat unterschrieben wurde"
77
+ # Date
78
+ mandate_date_of_signature: Date.new(2011,1,25),
79
+
80
+ # Local instrument, in German "Lastschriftart"
81
+ # One of this strings:
82
+ # 'CORE' ("Basis-Lastschrift")
83
+ # 'B2B' ("Firmen-Lastschrift")
84
+ local_instrument: 'CORE',
85
+
86
+ # Sequence type
87
+ # One of this strings:
88
+ # 'FRST' ("Erst-Lastschrift")
89
+ # 'RCUR' ("Folge-Lastschrift")
90
+ # 'OOFF' ("Einmalige Lastschrift")
91
+ # 'FNAL' ("Letztmalige Lastschrift")
92
+ sequence_type: 'OOFF',
93
+
94
+ # OPTIONAL: Requested collection date, in German "Fälligkeitsdatum der Lastschrift"
95
+ # Date
96
+ requested_date: Date.new(2013,9,5),
97
+
98
+ # OPTIONAL: Enables or disables batch booking, in German "Sammelbuchung / Einzelbuchung"
99
+ batch_booking: true
100
+ )
101
+ sdd.add_transaction ...
102
+
103
+ # Last: create XML string
104
+ xml_string = sdd.to_xml
105
+ ```
106
+
107
+
108
+ How to create the XML for **Credit Transfer Initiation** (in german: "Überweisungen")
109
+
110
+ ```ruby
111
+ # First: Create the main object
112
+ sct = SEPA::CreditTransfer.new(
113
+ # Name of the initiating party and debitor, in German: "Auftraggeber"
114
+ # String, max. 70 char
115
+ name: 'Schuldner GmbH',
116
+
117
+ # Business Identifier Code (SWIFT-Code) of the debitor
118
+ # String, 8 or 11 char
119
+ bic: 'BANKDEFFXXX',
120
+
121
+ # International Bank Account Number of the debitor
122
+ # String, max. 34 chars
123
+ iban: 'DE87200500001234567890'
124
+ )
125
+
126
+ # Second: Add transactions
127
+ sct.add_transaction(
128
+ # Name of the creditor, in German: "Zahlungsempfänger"
129
+ # String, max. 70 char
130
+ name: 'Telekomiker AG',
131
+
132
+ # Business Identifier Code (SWIFT-Code) of the creditor's account
133
+ # String, 8 or 11 char
134
+ bic: 'PBNKDEFF370',
135
+
136
+ # International Bank Account Number of the creditor's account
137
+ # String, max. 34 chars
138
+ iban: 'DE37112589611964645802',
139
+
140
+ # Amount in EUR
141
+ # Number with two decimal digit
142
+ amount: 102.50,
143
+
144
+ # OPTIONAL: End-To-End-Identification, will be submitted to the creditor
145
+ # String, max. 35 char
146
+ reference: 'XYZ-1234/123',
147
+
148
+ # OPTIONAL: Unstructured remittance Information, in German "Verwendungszweck"
149
+ # String, max. 140 char
150
+ remittance_information: 'Rechnung vom 22.08.2013'
151
+ )
152
+ sct.add_transaction ...
153
+
154
+ # Last: create XML string
155
+ xml_string = sct.to_xml
156
+ ```
157
+
158
+
159
+ ## Changelog
160
+
161
+ https://github.com/salesking/sepa_king/releases
162
+
163
+
164
+ ## Resources
165
+
166
+ * http://www.ebics.de/index.php?id=77
167
+ * SalesKing: http://salesking.eu
168
+
169
+
170
+ ## License
171
+
172
+ Released under the MIT license
173
+
174
+ Copyright (c) 2013 Georg Leciejewski (SalesKing), Georg Ledermann (https://github.com/ledermann)
data/Rakefile CHANGED
@@ -3,4 +3,4 @@ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task default: :spec
data/lib/sepa_king.rb CHANGED
@@ -5,6 +5,7 @@ require 'builder'
5
5
  require 'iban-tools'
6
6
 
7
7
  require 'sepa_king/converter'
8
+ require 'sepa_king/validator'
8
9
  require 'sepa_king/account'
9
10
  require 'sepa_king/account/debtor_account'
10
11
  require 'sepa_king/account/creditor_account'
@@ -1,18 +1,19 @@
1
1
  # encoding: utf-8
2
2
  module SEPA
3
3
  class Account
4
- include ActiveModel::Model
4
+ include ActiveModel::Validations
5
5
  extend Converter
6
6
 
7
7
  attr_accessor :name, :iban, :bic
8
- convert :name, :to => :text
8
+ convert :name, to: :text
9
9
 
10
- validates_presence_of :name, :iban, :bic
11
- validates_length_of :name, :maximum => 70
12
- validates_length_of :bic, :within => 8..11
10
+ validates_length_of :name, within: 1..70
11
+ validates_with BICValidator, IBANValidator
13
12
 
14
- validate do |t|
15
- errors.add(:iban, 'is invalid') unless IBANTools::IBAN.valid?(t.iban.to_s)
13
+ def initialize(attributes = {})
14
+ attributes.each do |name, value|
15
+ send("#{name}=", value)
16
+ end
16
17
  end
17
18
  end
18
19
  end
@@ -1,8 +1,8 @@
1
1
  # encoding: utf-8
2
2
  module SEPA
3
3
  class CreditorAccount < Account
4
- attr_accessor :identifier
4
+ attr_accessor :creditor_identifier
5
5
 
6
- validates_length_of :identifier, :is => 18
6
+ validates_with CreditorIdentifierValidator
7
7
  end
8
8
  end
@@ -3,11 +3,11 @@
3
3
  module SEPA
4
4
  class Message
5
5
  attr_reader :account, :transactions
6
- class_attribute :account_class, :transaction_class
6
+ class_attribute :account_class, :transaction_class, :xml_main_tag
7
7
 
8
8
  def initialize(account_options={})
9
9
  @transactions = []
10
- @account = self.account_class.new(account_options)
10
+ @account = account_class.new(account_options)
11
11
  end
12
12
 
13
13
  def add_transaction(options)
@@ -16,12 +16,22 @@ module SEPA
16
16
  @transactions << transaction
17
17
  end
18
18
 
19
+ # @return [String] xml
19
20
  def to_xml
20
21
  raise RuntimeError.new(account.errors.full_messages.join("\n")) unless account.valid?
22
+
23
+ builder = Builder::XmlMarkup.new indent: 2
24
+ builder.instruct!
25
+ builder.Document(xml_schema) do
26
+ builder.__send__(xml_main_tag) do
27
+ build_group_header(builder)
28
+ build_payment_informations(builder)
29
+ end
30
+ end
21
31
  end
22
32
 
23
- def amount_total
24
- transactions.inject(0) { |sum, t| sum + t.amount }
33
+ def amount_total(selected_transactions=transactions)
34
+ selected_transactions.inject(0) { |sum, t| sum + t.amount }
25
35
  end
26
36
 
27
37
  private
@@ -4,80 +4,87 @@ module SEPA
4
4
  class CreditTransfer < Message
5
5
  self.account_class = DebtorAccount
6
6
  self.transaction_class = CreditTransferTransaction
7
+ self.xml_main_tag = 'CstmrCdtTrfInitn'
7
8
 
8
- def to_xml
9
- super
9
+ private
10
+ # @return {Hash<Symbol=>String>} xml schema information used in output xml
11
+ def xml_schema
12
+ { :xmlns => 'urn:iso:std:iso:20022:tech:xsd:pain.001.002.03',
13
+ :'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
14
+ :'xsi:schemaLocation' => 'urn:iso:std:iso:20022:tech:xsd:pain.001.002.03 pain.001.002.03.xsd' }
15
+ end
10
16
 
11
- builder = Builder::XmlMarkup.new :indent => 2
12
- builder.instruct!
13
- builder.Document :xmlns => 'urn:iso:std:iso:20022:tech:xsd:pain.001.002.03',
14
- :'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
15
- :'xsi:schemaLocation' => 'urn:iso:std:iso:20022:tech:xsd:pain.001.002.03 pain.001.002.03.xsd' do
16
- builder.CstmrCdtTrfInitn do
17
- build_group_header(builder)
18
- build_payment_information(builder)
19
- end
17
+ # Find groups of transactions which share the same values of some attributes
18
+ def grouped_transactions
19
+ transactions.group_by do |transaction|
20
+ { requested_date: transaction.requested_date,
21
+ batch_booking: transaction.batch_booking
22
+ }
20
23
  end
21
24
  end
22
25
 
23
- private
24
- def build_payment_information(builder)
25
- builder.PmtInf do
26
- builder.PmtInfId(payment_information_identification)
27
- builder.PmtMtd('TRF')
28
- builder.BtchBookg(true)
29
- builder.NbOfTxs(transactions.length)
30
- builder.CtrlSum('%.2f' % amount_total)
31
- builder.PmtTpInf do
32
- builder.SvcLvl do
33
- builder.Cd('SEPA')
26
+ def build_payment_informations(builder)
27
+ # Build a PmtInf block for every group of transactions
28
+ grouped_transactions.each do |group, transactions|
29
+ # All transactions with the same requested_date are placed into the same PmtInf block
30
+ builder.PmtInf do
31
+ builder.PmtInfId(payment_information_identification)
32
+ builder.PmtMtd('TRF')
33
+ builder.BtchBookg(group[:batch_booking])
34
+ builder.NbOfTxs(transactions.length)
35
+ builder.CtrlSum('%.2f' % amount_total(transactions))
36
+ builder.PmtTpInf do
37
+ builder.SvcLvl do
38
+ builder.Cd('SEPA')
39
+ end
34
40
  end
35
- end
36
- builder.ReqdExctnDt(Date.today.next.iso8601)
37
- builder.Dbtr do
38
- builder.Nm(account.name)
39
- end
40
- builder.DbtrAcct do
41
- builder.Id do
42
- builder.IBAN(account.iban)
41
+ builder.ReqdExctnDt(group[:requested_date].iso8601)
42
+ builder.Dbtr do
43
+ builder.Nm(account.name)
43
44
  end
44
- end
45
- builder.DbtrAgt do
46
- builder.FinInstnId do
47
- builder.BIC(account.bic)
45
+ builder.DbtrAcct do
46
+ builder.Id do
47
+ builder.IBAN(account.iban)
48
+ end
49
+ end
50
+ builder.DbtrAgt do
51
+ builder.FinInstnId do
52
+ builder.BIC(account.bic)
53
+ end
54
+ end
55
+ builder.ChrgBr('SLEV')
56
+
57
+ transactions.each do |transaction|
58
+ build_transaction(builder, transaction)
48
59
  end
49
60
  end
50
- builder.ChrgBr('SLEV')
51
- build_transactions(builder)
52
61
  end
53
62
  end
54
63
 
55
- def build_transactions(builder)
56
- transactions.each do |transaction|
57
- builder.CdtTrfTxInf do
58
- builder.PmtId do
59
- builder.EndToEndId(transaction.reference || 'NOTPROVIDED')
60
- end
61
- builder.Amt do
62
- builder.InstdAmt('%.2f' % transaction.amount, :Ccy => 'EUR')
63
- end
64
- builder.CdtrAgt do
65
- builder.FinInstnId do
66
- builder.BIC(transaction.bic)
67
- end
68
- end
69
- builder.Cdtr do
70
- builder.Nm(transaction.name)
64
+ def build_transaction(builder, transaction)
65
+ builder.CdtTrfTxInf do
66
+ builder.PmtId do
67
+ builder.EndToEndId(transaction.reference)
68
+ end
69
+ builder.Amt do
70
+ builder.InstdAmt('%.2f' % transaction.amount, Ccy: 'EUR')
71
+ end
72
+ builder.CdtrAgt do
73
+ builder.FinInstnId do
74
+ builder.BIC(transaction.bic)
71
75
  end
72
- builder.CdtrAcct do
73
- builder.Id do
74
- builder.IBAN(transaction.iban)
75
- end
76
+ end
77
+ builder.Cdtr do
78
+ builder.Nm(transaction.name)
79
+ end
80
+ builder.CdtrAcct do
81
+ builder.Id do
82
+ builder.IBAN(transaction.iban)
76
83
  end
77
- if transaction.remittance_information
78
- builder.RmtInf do
79
- builder.Ustrd(transaction.remittance_information)
80
- end
84
+ end
85
+ if transaction.remittance_information
86
+ builder.RmtInf do
87
+ builder.Ustrd(transaction.remittance_information)
81
88
  end
82
89
  end
83
90
  end