class SEPA::Message

Attributes

account[R]
grouped_transactions[R]

Public Class Methods

new(account_options={}) click to toggle source
# File lib/sepa_king/message.rb, line 24
def initialize(account_options={})
  @grouped_transactions = {}
  @account = account_class.new(account_options)
end

Public Instance Methods

add_transaction(options) click to toggle source
# File lib/sepa_king/message.rb, line 29
def add_transaction(options)
  transaction = transaction_class.new(options)
  raise ArgumentError.new(transaction.errors.full_messages.join("\n")) unless transaction.valid?
  @grouped_transactions[transaction_group(transaction)] ||= []
  @grouped_transactions[transaction_group(transaction)] << transaction
end
amount_total(selected_transactions=transactions) click to toggle source
# File lib/sepa_king/message.rb, line 58
def amount_total(selected_transactions=transactions)
  selected_transactions.inject(0) { |sum, t| sum + t.amount }
end
batch_id(transaction_reference) click to toggle source

Returns the id of the batch to which the given transaction belongs Identified based upon the reference of the transaction

# File lib/sepa_king/message.rb, line 106
def batch_id(transaction_reference)
  grouped_transactions.each do |group, transactions|
    if transactions.select { |transaction| transaction.reference == transaction_reference }.any?
      return payment_information_identification(group)
    end
  end
end
batches() click to toggle source
# File lib/sepa_king/message.rb, line 114
def batches
  grouped_transactions.keys.collect { |group| payment_information_identification(group) }
end
creation_date_time() click to toggle source

Get creation date time for the message (with fallback to Time.now.iso8601)

# File lib/sepa_king/message.rb, line 100
def creation_date_time
  @creation_date_time ||= Time.now.iso8601
end
creation_date_time=(value) click to toggle source

Set creation date time for the message p.s. Rabobank in the Netherlands only accepts the more restricted format [0-9]4[0-9]2,2[0-9]2,2[0-9]2,2[0-9]2,2[0-9]{2,2}

# File lib/sepa_king/message.rb, line 90
def creation_date_time=(value)
  raise ArgumentError.new('creation_date_time must be a string!') unless value.is_a?(String)

  regex = /[0-9]{4}[-][0-9]{2,2}[-][0-9]{2,2}(?:\s|T)[0-9]{2,2}[:][0-9]{2,2}[:][0-9]{2,2}/
  raise ArgumentError.new("creation_date_time does not match #{regex}!") unless value.match(regex)

  @creation_date_time = value
end
message_identification() click to toggle source

Get unique identifer for the message (with fallback to a random string)

# File lib/sepa_king/message.rb, line 84
def message_identification
  @message_identification ||= "SEPA-KING/#{SecureRandom.hex(11)}"
end
message_identification=(value) click to toggle source

Set unique identifer for the message

# File lib/sepa_king/message.rb, line 74
def message_identification=(value)
  raise ArgumentError.new('message_identification must be a string!') unless value.is_a?(String)

  regex = /\A([A-Za-z0-9]|[\+|\?|\/|\-|\:|\(|\)|\.|\,|\'|\ ]){1,35}\z/
  raise ArgumentError.new("message_identification does not match #{regex}!") unless value.match(regex)

  @message_identification = value
end
schema_compatible?(schema_name) click to toggle source
# File lib/sepa_king/message.rb, line 62
def schema_compatible?(schema_name)
  raise ArgumentError.new("Schema #{schema_name} is unknown!") unless self.known_schemas.include?(schema_name)

  case schema_name
    when PAIN_001_002_03, PAIN_008_002_02, PAIN_001_001_03, PAIN_001_001_03_CH_02
      account.bic.present? && transactions.all? { |t| t.schema_compatible?(schema_name) }
    when PAIN_001_003_03, PAIN_008_003_02, PAIN_008_001_02
      transactions.all? { |t| t.schema_compatible?(schema_name) }
  end
end
to_xml(schema_name=self.known_schemas.first) click to toggle source

@return [String] xml

# File lib/sepa_king/message.rb, line 41
def to_xml(schema_name=self.known_schemas.first)
  raise SEPA::Error.new(errors.full_messages.join("\n")) unless valid?
  raise SEPA::Error.new("Incompatible with schema #{schema_name}!") unless schema_compatible?(schema_name)

  builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |builder|
    builder.Document(xml_schema(schema_name)) do
      builder.__send__(xml_main_tag) do
        build_group_header(builder)
        build_payment_informations(builder)
      end
    end
  end

  validate_final_document!(builder.doc, schema_name)
  builder.to_xml
end
transactions() click to toggle source
# File lib/sepa_king/message.rb, line 36
def transactions
  grouped_transactions.values.flatten
end

Private Instance Methods

build_group_header(builder) click to toggle source
# File lib/sepa_king/message.rb, line 134
def build_group_header(builder)
  builder.GrpHdr do
    builder.MsgId(message_identification)
    builder.CreDtTm(creation_date_time)
    builder.NbOfTxs(transactions.length)
    builder.CtrlSum('%.2f' % amount_total)
    builder.InitgPty do
      builder.Nm(account.name)
      builder.Id do
        builder.OrgId do
          builder.Othr do
            builder.Id(account.creditor_identifier)
          end
        end
      end if account.respond_to? :creditor_identifier
    end
  end
end
payment_information_identification(group) click to toggle source

Unique and consecutive identifier (used for the <PmntInf> blocks)

# File lib/sepa_king/message.rb, line 154
def payment_information_identification(group)
  "#{message_identification}/#{grouped_transactions.keys.index(group)+1}"
end
transaction_group(transaction) click to toggle source

Returns a key to determine the group to which the transaction belongs

# File lib/sepa_king/message.rb, line 159
def transaction_group(transaction)
  transaction
end
validate_final_document!(document, schema_name) click to toggle source
# File lib/sepa_king/message.rb, line 163
def validate_final_document!(document, schema_name)
  xsd = Nokogiri::XML::Schema(File.read(File.expand_path("../../../lib/schema/#{schema_name}.xsd", __FILE__)))
  errors = xsd.validate(document).map { |error| error.message }
  raise SEPA::Error.new("Incompatible with schema #{schema_name}: #{errors.join(', ')}") if errors.any?
end
xml_schema(schema_name) click to toggle source

@return {Hash<Symbol=>String>} xml schema information used in output xml

# File lib/sepa_king/message.rb, line 120
def xml_schema(schema_name)
  return {
    :xmlns                => "urn:iso:std:iso:20022:tech:xsd:#{schema_name}",
    :'xmlns:xsi'          => 'http://www.w3.org/2001/XMLSchema-instance',
    :'xsi:schemaLocation' => "urn:iso:std:iso:20022:tech:xsd:#{schema_name} #{schema_name}.xsd"
  } unless schema_name == PAIN_001_001_03_CH_02

  {
    xmlns:                'http://www.six-interbank-clearing.com/de/pain.001.001.03.ch.02.xsd',
    'xmlns:xsi':          'http://www.w3.org/2001/XMLSchema-instance',
    'xsi:schemaLocation': 'http://www.six-interbank-clearing.com/de/pain.001.001.03.ch.02.xsd  pain.001.001.03.ch.02.xsd'
  }
end