defmodule FinTex.Command.Synchronization do @moduledoc false alias FinTex.Command.AbstractCommand alias FinTex.Command.Sequencer alias FinTex.Model.Account alias FinTex.Model.Dialog alias FinTex.Model.TANScheme alias FinTex.Segment.HITANS alias FinTex.Segment.HKEND alias FinTex.Segment.HKIDN alias FinTex.Segment.HKSYN alias FinTex.Segment.HKVVB alias FinTex.Segment.HNHBK alias FinTex.Segment.HNHBS alias FinTex.Segment.HNSHA alias FinTex.Segment.HNSHK @allowed_methods 3920 use AbstractCommand def initialize_dialog(bank, login, client_id, pin, tan_scheme_sec_func \\ nil) do seq = Sequencer.new(bank, login, client_id, pin) case seq |> Sequencer.needs_synchronization? do true -> seq |> synchronize(tan_scheme_sec_func) false -> seq end end def terminate_dialog(seq) do request_segments = [ %HNHBK{}, %HNSHK{}, %HKEND{}, %HNSHA{}, %HNHBS{} ] {:ok, _} = seq |> Sequencer.call_http(request_segments) end defp synchronize(seq, tan_scheme_sec_func) do request_segments = [ %HNHBK{}, %HNSHK{}, %HKIDN{}, %HKVVB{}, %HKSYN{}, %HNSHA{}, %HNHBS{} ] {:ok, response} = seq |> Sequencer.call_http(request_segments) bpd = response[4] |> Enum.group_by(HashDict.new, fn [[name | _] | _] -> name end) pintan = bpd |> Dict.get("HKPIN" |> control_structure_to_bpd) pintan = case pintan do nil -> bpd |> Enum.map(fn {k, v} -> {k |> bpd_to_control_structure, v} end) |> Enum.into(HashDict.new) _ -> pintan |> Enum.at(0) |> Enum.at(4) |> Enum.at(5) |> Stream.map(fn {k, _} -> k end) |> Stream.map(fn name -> {name, bpd[name |> control_structure_to_bpd]} end) |> Enum.into(HashDict.new) end tan_scheme_sec_funcs = response[:HIRMS] |> messages |> Stream.filter(fn [code | _] -> code === @allowed_methods end) |> Stream.map(fn [_, _, _ | params] -> params end) |> Enum.at(0) |> Enum.into(HashSet.new) supported_tan_schemes = pintan |> Dict.get("HKTAN") |> Stream.flat_map(&HITANS.to_tan_schemes(&1)) |> Stream.filter(fn %TANScheme{:sec_func => sec_func} -> tan_scheme_sec_funcs |> Set.member?(sec_func) end) |> Enum.uniq(fn %TANScheme{:sec_func => sec_func} -> sec_func end) seq = seq |> Sequencer.inc |> Sequencer.update(client_system_id(response), dialog_id(response), bpd, pintan, supported_tan_schemes) request_segments = [ %HNHBK{}, %HNSHK{}, %HKEND{}, %HNSHA{}, %HNHBS{} ] {:ok} = seq |> Sequencer.call_http(request_segments, ignore_response: true) new_tan_scheme_sec_func = cond do tan_scheme_sec_func == nil and tan_scheme_sec_funcs |> HashSet.to_list == [] -> nil tan_scheme_sec_func == nil -> tan_scheme_sec_funcs |> HashSet.to_list |> Enum.at(0) :else -> tan_scheme_sec_func end seq = seq |> Sequencer.reset(new_tan_scheme_sec_func) request_segments = [ %HNHBK{}, %HNSHK{}, %HKIDN{}, %HKVVB{}, %HNSHA{}, %HNHBS{} ] {:ok, response} = seq |> Sequencer.call_http(request_segments) seq = seq |> Sequencer.update(dialog_id(response)) |> Sequencer.inc accounts = seq |> Sequencer.dialog |> accounts(response[:HIUPD]) {seq, accounts} end defp client_system_id(response) do response[:HISYN] |> Enum.at(0) |> Enum.at(-1) end defp accounts( %Dialog{ bank: bank, supported_tan_schemes: supported_tan_schemes, bpd: bpd, pintan: pintan }, user_params) do offset = case bank.version do "300" -> 2 _ -> 0 end user_params |> Stream.map(fn u -> account = %Account{ :account_number => u |> Enum.at(1) |> Enum.at(0), :subaccount_id => u |> Enum.at(1) |> Enum.at(1), :blz => u |> Enum.at(1) |> Enum.at(3), :currency => u |> Enum.at(3 + offset), :owner => [u |> Enum.at(4 + offset), u |> Enum.at(5 + offset)] |> Enum.join(" ") |> String.split |> Stream.map(&String.capitalize/1) |> Enum.join(" ") |> String.strip, :name => u |> Enum.at(6 + offset), :bank_name => bpd |> Dict.get("HIBPA") |> Enum.at(0) |> Enum.at(3) |> String.strip, :supported_transactions => u |> Stream.drop(8 + offset) |> Stream.map(fn [transaction, _] -> transaction end) |> Stream.filter(fn transaction -> pintan |> Dict.has_key?(transaction) end) |> Enum.uniq, :supported_tan_schemes => supported_tan_schemes, :preferred_tan_scheme => supported_tan_schemes |> Stream.map(fn %TANScheme{:sec_func => sec_func} -> sec_func end) |> Enum.at(0) } # add IBAN if available iban = case Enum.at(u, 2) do "" -> nil els -> els end case bank.version do "300" -> %Account{account | :iban => iban} _ -> account end end) |> Stream.map(fn account = %Account{:account_number => account_number} -> {account_number, account} end) |> Enum.into(HashDict.new) end end