defmodule Burox.Request.Encoder do @moduledoc """ Este módulo contiene la especificación de los datos que pueden ir en la petición del Buró de Crédito """ import Burox.Request.Config alias Burox.Request @doc """ Convierte una estructura de respuesta al formato recibido por el buró de crédito """ @spec encode_buro(Request.t(), String.t()) :: {:ok, term} | {:error, term} def encode_buro(peticion, codigo_de_producto) do body = if peticion.autenticacion.cuenta_con_tarjeta_de_credito != nil do "#{build_authentication(peticion.autenticacion)}" <> "#{build_header(codigo_de_producto)}" <> "#{build_body(peticion)}" else "#{build_header(codigo_de_producto)}" <> "#{build_body(peticion)}" end body <> build_end(body) end # Crea el encabezado de la petición, defp build_header(codigo_de_producto) do {buro_user, buro_password, version} = case codigo_de_producto do "107" -> # El servicio de prospector solo esta disponible en la versión 11 {Application.get_env(:burox, :buro_user_prospector), Application.get_env(:burox, :buro_password_prospector), "11"} _ -> {Application.get_env(:burox, :buro_user), Application.get_env(:burox, :buro_password), "13"} end if is_nil(buro_user) or is_nil(buro_password) do raise Burox.Error, message: "Deben configurarse las credenciales del Buró de Crédito" end "INTL" <> version <> " " <> codigo_de_producto <> "MX" # Clave de pais <> "0000" # Reservado <> buro_user <> buro_password <> "I" # Tipo de responsabilidad(I: Individual) <> "CC" # Tipo de contrato <> "MX" # Mondea del crédito <> "000000000" # Importe del contrato <> "SP" # Idioma (SP: ingles) <> "01" # Tipo de salida (01: Archivo de cadena de datos) <> " " # Tamaño de bloque de salida <> " " # Identificación de la impresora <> "0000000" # Reservado para uso futuro end # Crea el cuerpo de la petición # donde van codificados los datos de la persona defp build_body(request) do request_map = Map.from_struct(request) # Reduce sobre la lista de configuración y no sobre los # parametros, para mantener el orden en la cadena de salida Enum.reduce(sections_list(), "", fn {segmento, config}, acc -> values = request_map[segmento] # ¿Hay valores del segmento? if values != nil do # Agrega la llave del segmento acc = acc <> config[:key] # Agrega los valores acc <> build_tag_values(config[:tags], values) end end) end # Función para construir los valores de segmento de autenticación del cliente defp build_authentication(values) do auth = autenticacion() acc = "AU03RCN000120125 " # ¿Hay valores del segmento? if values != nil do # Agrega los valores acc <> build_tag_values(auth[:tags], values) end end # Funcion para construir el segmento de fin de petición defp build_end(request_string) do # Retorna el tamaño de la cadena de petición # Siempre es una cadena de 5 caracteres, si hace # falta se agregan ceros a la izquierda number_with_pad = request_string |> String.length() # 17 del tamaño de este segmento |> Kernel.+(100_017) |> to_string() |> String.slice(1..-1) "ES05" <> number_with_pad <> "0002**" end # Función para construir los valores de las etiquetas de un segmento # Nombre + tamaño + valor defp build_tag_values(tags, values) do Enum.reduce(tags, "", fn {key, tag}, acc -> value = Map.get(values, key) if value != nil do acc <> tag <> size_of(value) <> value else acc end end) end # Retorna el numero de caracteres de una cadena # El valor retornado siempre estará expresado en dos caracteres # i.e '12' # Si el valor es menor a 10, comenzará con 0 # i.e '02' defp size_of(string) do size = String.length(string) cond do size < 10 -> "0#{size}" size > 99 -> raise "El valor es demasiado grande para ser codificado" true -> "#{size}" end end end