lib/vatchex_greece.ex

# SPDX-FileCopyrightText: 2023 Isaak Tsalicoglou <isaak@waseigo.com>
# SPDX-License-Identifier: Apache-2.0

defmodule APIauth do
  @moduledoc """
  Defines the `%APIauth{}` struct used to store authentication data for SOAP API calls.
  """
  @moduledoc since: "0.7.0"
  @enforce_keys [:username, :password, :afm_called_by]
  defstruct @enforce_keys
end

defmodule NACEactivity do
  @moduledoc """
  Defines the `%NACEactivity{}` struct to store parsed information about company activities.
  """
  @enforce_keys [:code]
  defstruct code: nil,
            # secondary activity, if not specified otherwise with "1" for primary activity
            prio: 2,
            # description of the activity; cosmetic
            descr: nil,
            # "ΚΥΡΙΑ" for primary, "ΔΕΥΤΕΡΕΥΟΥΣΑ" for secondary; cosmetic
            prio_text: nil
end

defmodule GSISdata do
  @moduledoc """
  Defines the `%GSISdata{}` struct used by the functions of the `Process` module to process and store the response of the GSIS SOAP API in a usable format.
  """
  @moduledoc since: "0.7.0"
  @enforce_keys [:afm]
  defstruct [
    :afm,
    :as_on_date,
    :doy,
    :doy_descr,
    :i_ni_flag_descr,
    :deactivation_flag,
    :deactivation_flag_descr,
    :firm_flag_descr,
    :onomasia,
    :commer_title,
    :legal_status_descr,
    :postal_address,
    :postal_address_no,
    :postal_zip_code,
    :postal_area_description,
    :regist_date,
    :stop_date,
    :normal_vat_system_flag,
    activities: []
  ]
end

defmodule Results do
  @moduledoc """
  Defines the `%Results{}` struct that gets progressively enriched with information from the API.
  """
  @enforce_keys [:auth]
  defstruct [
    :auth,
    :data,
    :request,
    :response,
    errors: %{}
  ]
end

defmodule VatchexGreece do
  require EEx

  @moduledoc """
  Main module of `VatchexGreece` that contains high-level functions that the
  user typically will interact with.
  """
  @moduledoc since: "0.1.0"

  alias VatchexGreece.{Request, Process, Validate}

  @doc """
  Define a new struct that will be manipulated from containing only the minimal information required to query the SOAP API, to containing the complete request, the SOAP API response, and the parsed information thereof into the corresponding structs.

  """
  @doc since: "0.8.0"
  @doc deprecated: "Instead of chaining this with get/1, use fetch/1"

  def new(afm_called_for, username, password, afm_called_by) do
    auth = %APIauth{
      username: username,
      password: password,
      afm_called_by: afm_called_by
    }

    data = %GSISdata{afm: afm_called_for}

    %Results{auth: auth, data: data}
  end

  @doc """
  Pull information from the web service for the target VAT ID `afm_called_for` defined in the `Results` struct that has first been created with `new/4`.

  Note: the function minimizes and checks the source and target VAT IDs, and only
  sends the request to the API if both minimized VAT IDs are valid.
  """

  @doc since: "0.7.0"
  @doc deprecated: "Instead of chaining this after new/4, use fetch/1"
  def get(%Results{} = input) do
    input
    |> Validate.all_valid()
    |> Request.prepare()
    |> Request.post()
    |> Process.parse()
  end

  @doc """
  Pull information from the web service for the target VAT ID `:afm_called_for`, by the source VAT ID `:afm_called_by`, with authentication parameters `:username` and `:password`.

  To create the authentication credentials:
  1. Sign up to the ["wspublicreg" service](https://www1.aade.gr/webtax/wspublicreg/faces/pages/wspublicreg/menu.xhtml) using your TAXISnet credentials.
  2. Create "special access codes" through the ["Διαχείριση Ειδικών Κωδικών" application on TAXISnet](https://www1.aade.gr/sgsisapps/tokenservices/protected/displayConsole.htm).

  Regarding the target and source VAT IDs, you will typically pass a valid 9-digit VAT ID, with or without the "EL" prefix. The "EL" prefix will get stripped. Older VAT IDs only have 8 digits, and will get leading-zero-padded to 9 digits.

  Note: the function minimizes and checks the source and target VAT IDs, and only sends the request to the API if both minimized VAT IDs are valid.
  """

  @doc since: "0.8.0"
  def fetch(
        afm_called_for: target,
        username: u,
        password: p,
        afm_called_by: source
      ) do
    x =
      target
      |> new(u, p, source)
      |> do_get()

    case x do
      {:ok, %Results{data: data}} -> {:ok, mapize(data)}
      {:error, %Results{errors: errors}} -> {:error, errors}
    end
  end

  @doc """
  Same as `VatchexGreece.fetch/1`, but the resulting tuple is unwrapped. Returns `nil` if there were errors, otherwise it returns the resulting data.
  """

  @doc since: "0.8.0"
  def fetch!(
        afm_called_for: target,
        username: u,
        password: p,
        afm_called_by: source
      ) do
    x =
      fetch(
        afm_called_for: target,
        username: u,
        password: p,
        afm_called_by: source
      )

    case x do
      {:ok, data} -> data
      {:error, _} -> nil
    end
  end

  defp do_get(%Results{} = input) do
    input
    |> Validate.all_valid()
    |> Request.prepare()
    |> Request.post()
    |> Process.parse()
  end

  defp mapize(x) when is_struct(x) do
    x
    |> Map.from_struct()
    |> mapize()
  end

  defp mapize(x) when is_map(x) do
    x
    |> Enum.map(fn {k, v} -> {k, mapize(v)} end)
    |> Map.new()
  end

  defp mapize(x) when is_list(x), do: Enum.map(x, &mapize/1)

  defp mapize(x), do: x
end