diff --git a/.formatter.exs b/.formatter.exs index d2cda26..0cf93db 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,4 +1,5 @@ # Used by "mix format" [ - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"], + line_length: 140 ] diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index f4b466b..9fb5222 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -4,6 +4,11 @@ on: pull_request: branches: [develop] +env: + MIX_ENV: test + LANG: C.UTF-8 + SZAMLAZZ_AGENT_KEY: ${{ secrets.SZAMLAZZ_AGENT_KEY_TEST }} + jobs: build: name: Build and test @@ -77,8 +82,4 @@ jobs: - name: Compile code run: mix compile --warnings-as-errors - name: Run tests - run: mix test - - env: - MIX_ENV: test - LANG: C.UTF-8 + run: mix test --include external:true diff --git a/.gitignore b/.gitignore index aaf6b49..0a657a8 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ ex_szamlazz_hu-*.tar # Dialyxir PLT files (special placement for CI caching) /priv/plts/*.plt /priv/plts/*.plt.hash + +/.elixir_ls diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..ffb36d1 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +elixir 1.14.3-otp-25 +erlang 25.0.1 diff --git a/README.md b/README.md index a553a71..1d5c422 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ExSzamlazzHu -**TODO: Add description** +A very thing wrapper around Szamlazz.hu API. ## Installation @@ -18,4 +18,3 @@ end Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) and published on [HexDocs](https://hexdocs.pm). Once published, the docs can be found at . - diff --git a/config/config.exs b/config/config.exs new file mode 100644 index 0000000..255baf1 --- /dev/null +++ b/config/config.exs @@ -0,0 +1,5 @@ +import Config + +config :ex_szamlazz_hu, szamlazz_hu_api_url: "https://www.szamlazz.hu/szamla/" + +import_config "#{Mix.env()}.exs" diff --git a/config/dev.exs b/config/dev.exs new file mode 100644 index 0000000..361b54a --- /dev/null +++ b/config/dev.exs @@ -0,0 +1,3 @@ +import Config + +config :tesla, :adapter, Tesla.Adapter.Hackney diff --git a/config/test.exs b/config/test.exs new file mode 100644 index 0000000..361b54a --- /dev/null +++ b/config/test.exs @@ -0,0 +1,3 @@ +import Config + +config :tesla, :adapter, Tesla.Adapter.Hackney diff --git a/lib/ex_szamlazz_hu.ex b/lib/ex_szamlazz_hu.ex index e98ee93..9ca8566 100644 --- a/lib/ex_szamlazz_hu.ex +++ b/lib/ex_szamlazz_hu.ex @@ -1,18 +1,125 @@ defmodule ExSzamlazzHu do + alias ExSzamlazzHu.CreateInvoice + @moduledoc """ - Documentation for `ExSzamlazzHu`. + A very thin wrapper for the Szamlazz.hu API. + + ## Installation + + def deps do + [ + {:ex_szamlazz_hu, "~> 0.1.0"} + ] + end + + ExSzamlazzHu uses Tesla as a HTTP client. This means that you should configure Tesla to use the library your project is using. E.g. in your `config/config.exs`:application + + config :tesla, :adapter, Tesla.Adapter.Hackney + + See more at [Tesla](https://hexdocs.pm/tesla/readme.html#adapters). + + ## Features + + | Szamlazz.hu function | Is implemented? | + | ------------------------- | --------------- | + | Create invoice | ✅ | + | Reverse invoice | ❌ | + | Register credit entry | ❌ | + | Query invoice pdf | ❌ | + | Query invoice xml | ❌ | + | Delete pro forma invoice | ❌ | + | Create receipt | ❌ | + | Reverse receipt | ❌ | + | Query receipt | ❌ | + | Send receipt | ❌ | + | Query taxpayers | ❌ | + | Create supplier account | ❌ | + + ## Usage + + Even though this modules interface is in English (i.e. it provides functions like `create_invoice`), + but as a thin wrapper, the parameters follow the same shape as described in the [Szamlazz.hu API documentation](https://docs.szamlazz.hu/). + + The result of the call is a struct, which - among other things - contains the original response from the Szamlazz.hu API. + + For convenience, the result struct contains some other information, e.g. + - whether the call was successful or not + - the invoice identifier, if an invoice was created + - the path to the created invoice, if the PDF file download was requested + - and the error code, if an error occurred. + + Read more at the given feature's documentation. """ @doc """ Create invoice via Szamlazz.hu - ## Examples + The parameters follow the same shape as described in the [Szamlazz.hu API documentation](https://docs.szamlazz.hu/). + + ExSzamlazzHu.create_invoice(%{ + beallitasok: %{ + szamlaagentkulcs: "your Szamlazz.hu agent key", + eszamla: true, + szamlaLetoltes: false, + valaszVerzio: 1, + }, + fejlec: %{ + teljesitesDatum: "2023-11-12", + fizetesiHataridoDatum: "2023-11-12", + fizmod: "Stripe", + penznem: "EUR", + szamlaNyelve: "en", + megjegyzes: "", + rendelesSzam: "Skynet-O129A22", + dijbekero: false, + fizetve: true + }, + elado: %{}, + vevo: %{ + nev: "Sarah Connor", + orszag: "USA", + irsz: 32456, + telepules: "Los Angeles", + cim: "Engineering Drive", + email: "sarah.connor@no-skynet.com", + sendEmail: true + }, + tetelek: [ + %{ + megnevezes: "T-800 disassembly kit", + mennyiseg: 2, + mennyisegiEgyseg: "db", + nettoEgysegar: 100, + afakulcs: 27, + nettoErtek: 200, + afaErtek: 54, + bruttoErtek: 254, + } + ] + }) - iex> ExSzamlazzHu.create_invoice(%{}) - {:error, :not_implemented} + The result of the call is a struct, which - among other things - contains the original response from the Szamlazz.hu API. + {:ok, %ExSzamlazzHu.CreateInvoice.Result{success: true}} = ExSzamlazzHu.create_invoice(params) + + %ExSzamlazzHu.CreateInvoice.Result{ + success: true, # Indicates whether the request to the szamla.hu API was successful + raw_response: nil, # The raw response from the szamla.hu API + szlahu_id: nil, # The (internal) ID of the created invoice + szlahu_szamlaszam: nil, # The invoice number + szlahu_nettovegosszeg: nil, # The net amount of the created invoice + szlahu_bruttovegosszeg: nil, # The gross amount of the created invoice + szlahu_kintlevoseg: nil, # The amount not yet paid + szlahu_vevoifiokurl: nil, # The URL of the invoice + path_to_pdf_invoice: nil, # The path to the created invoice, if the PDF file was requested + szlahu_error: nil, # The error message, if any (and in Hungarian) + szlahu_error_code: nil, # The error code + szlahu_down: false # Indicates whether the Szamlazz.hu API is not available + } """ - def create_invoice(_params), do: {:error, :not_implemented} + @moduledoc since: "0.1.0" + + def create_invoice(params), do: CreateInvoice.run(params) def reverse_invoice(_params), do: {:error, :not_implemented} def register_credit_entry(_params), do: {:error, :not_implemented} def query_invoice_pdf(_params), do: {:error, :not_implemented} diff --git a/lib/modules/create_invoice.ex b/lib/modules/create_invoice.ex new file mode 100644 index 0000000..e804fea --- /dev/null +++ b/lib/modules/create_invoice.ex @@ -0,0 +1,133 @@ +defmodule ExSzamlazzHu.CreateInvoice do + @moduledoc false + + alias Tesla.Multipart + alias ExSzamlazzHu.CreateInvoice.InvoiceData + alias ExSzamlazzHu.CreateInvoice.Result + alias ExSzamlazzHu.Utils.TemporaryFile + + @spec run(params :: map()) :: {:ok, Result.t()} | {:error, :cannot_save_temporary_file} | {:error, any()} + def run(params) do + with invoice_data <- InvoiceData.parse(params), + xml <- InvoiceData.to_xml(invoice_data), + {:ok, file_path} <- save_temporary_file(xml), + {:ok, response} <- send_request(file_path), + {:ok, success, data} <- handle_response(response, invoice_data), + result <- compile_result(success, data, response) do + {:ok, result} + end + end + + defp save_temporary_file(xml) do + random_chars = for _ <- 1..5, into: "", do: <> + timestamp = DateTime.utc_now() |> DateTime.to_iso8601() + filename = "#{timestamp}_#{random_chars}.xml" + + TemporaryFile.save(filename, xml) + end + + defp send_request(file_path) do + body = + Multipart.new() + |> Multipart.add_content_type_param("charset=utf-8") + |> Multipart.add_file(file_path, name: "action-xmlagentxmlfile") + + url = Application.get_env(:ex_szamlazz_hu, :szamlazz_hu_api_url) + + Tesla.post(url, body) + end + + defp handle_response(%Tesla.Env{} = response, invoice_data) do + header_map = Map.new(response.headers) + + cond do + header_map["szlahu_down"] == "true" -> {:ok, false, %{szlahu_down: true}} + header_map["szlahu_error_code"] == nil -> handle_success_response(response, invoice_data) + true -> handle_error_response(response) + end + end + + defp handle_error_response(%Tesla.Env{} = response) do + header_map = Map.new(response.headers) + + result = %{ + szlahu_error: header_map["szlahu_error"], + szlahu_error_code: header_map["szlahu_error_code"] + } + + {:ok, false, result} + end + + defp handle_success_response(%Tesla.Env{} = response, invoice_data) do + header_map = Map.new(response.headers) + + params = %{ + szlahu_id: header_map["szlahu_id"], + szlahu_nettovegosszeg: header_map["szlahu_nettovegosszeg"], + szlahu_szamlaszam: header_map["szlahu_szamlaszam"], + szlahu_bruttovegosszeg: header_map["szlahu_bruttovegosszeg"], + szlahu_kintlevoseg: header_map["szlahu_kintlevoseg"], + szlahu_vevoifiokurl: header_map["szlahu_vevoifiokurl"] + } + + result = maybe_add_invoice_path_info(params, response, invoice_data) + + {:ok, true, result} + end + + defp maybe_add_invoice_path_info( + info, + %Tesla.Env{} = response, + %InvoiceData{beallitasok: %{szamlaLetoltes: true}} = invoice_data + ) do + random_chars = for _ <- 1..5, into: "", do: <> + timestamp = DateTime.utc_now() |> DateTime.to_iso8601() + filename = "#{timestamp}_#{random_chars}.pdf" + pdf = get_invoice_pdf_data(response, invoice_data) + + case TemporaryFile.save(filename, pdf) do + {:ok, path} -> Map.put(info, :invoice_file_path, path) + {:error, _} -> info + end + end + + defp get_invoice_pdf_data( + %Tesla.Env{} = response, + %InvoiceData{beallitasok: %{szamlaLetoltes: true, valaszVerzio: 1}} + ) do + response.body + end + + defp get_invoice_pdf_data( + %Tesla.Env{} = response, + %InvoiceData{beallitasok: %{szamlaLetoltes: true, valaszVerzio: 2}} + ) do + [pdf_tag] = Regex.run(~r/(.*)<\/pdf>/ims, response.body, capture: :first) + [_, base64_pdf] = String.split(pdf_tag, "") + [base64_pdf, _] = String.split(base64_pdf, "") + + base64_pdf = + base64_pdf + |> String.trim() + |> String.replace("\n", "") + + Base.decode64!(base64_pdf) + end + + defp compile_result(success, data, response) do + %Result{ + success: success, + raw_response: response, + szlahu_id: data[:szlahu_id], + szlahu_nettovegosszeg: data[:szlahu_nettovegosszeg], + szlahu_szamlaszam: data[:szlahu_szamlaszam], + szlahu_bruttovegosszeg: data[:szlahu_bruttovegosszeg], + szlahu_kintlevoseg: data[:szlahu_kintlevoseg], + szlahu_vevoifiokurl: data[:szlahu_vevoifiokurl], + path_to_pdf_invoice: data[:invoice_file_path], + szlahu_error: data[:szlahu_error], + szlahu_error_code: data[:szlahu_error_code], + szlahu_down: data[:szlahu_down] == true + } + end +end diff --git a/lib/modules/create_invoice/customer.ex b/lib/modules/create_invoice/customer.ex new file mode 100644 index 0000000..096eb84 --- /dev/null +++ b/lib/modules/create_invoice/customer.ex @@ -0,0 +1,80 @@ +defmodule ExSzamlazzHu.CreateInvoice.Customer do + @moduledoc false + + alias ExSzamlazzHu.CreateInvoice.CustomerLedger + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + @enforce_keys [ + :nev, + :irsz, + :telepules, + :cim + ] + defstruct [ + :nev, + :orszag, + :irsz, + :telepules, + :cim, + :email, + :sendEmail, + :adoalany, + :adoszam, + :csoportazonosito, + :adoszamEU, + :postazasiNev, + :postazasiOrszag, + :postazasiIrsz, + :postazasiTelepules, + :postazasiCim, + :vevoFokonyv, + :azonosito, + :alairoNeve, + :telefonszam, + :megjegyzes + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + __MODULE__ + |> struct(Map.drop(params, [:vevoFokonyv])) + |> Map.put(:vevoFokonyv, CustomerLedger.parse(params[:vevoFokonyv])) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + nev: &"#{&1}", + orszag: &"#{&1}", + irsz: &"#{&1}", + telepules: &"#{&1}", + cim: &"#{&1}", + email: &"#{&1}", + sendEmail: &"#{&1}", + adoalany: &"#{&1}", + adoszam: &"#{&1}", + csoportazonosito: &"#{&1}", + adoszamEU: &"#{&1}", + postazasiNev: &"#{&1}", + postazasiOrszag: &"#{&1}", + postazasiIrsz: &"#{&1}", + postazasiTelepules: &"#{&1}", + postazasiCim: &"#{&1}", + vevoFokonyv: &CustomerLedger.to_xml(&1), + azonosito: &"#{&1}", + alairoNeve: &"#{&1}", + telefonszam: &"#{&1}", + megjegyzes: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/customer_ledger.ex b/lib/modules/create_invoice/customer_ledger.ex new file mode 100644 index 0000000..e36da50 --- /dev/null +++ b/lib/modules/create_invoice/customer_ledger.ex @@ -0,0 +1,41 @@ +defmodule ExSzamlazzHu.CreateInvoice.CustomerLedger do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :konyvelesDatum, + :vevoAzonosito, + :vevoFokonyviSzam, + :folyamatosTelj, + :elszDatumTol, + :elszDatumIg + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + konyvelesDatum: &"#{&1}", + vevoAzonosito: &"#{&1}", + vevoFokonyviSzam: &"#{&1}", + folyamatosTelj: &"#{&1}", + elszDatumTol: &"#{&1}", + elszDatumIg: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/header.ex b/lib/modules/create_invoice/header.ex new file mode 100644 index 0000000..cf77642 --- /dev/null +++ b/lib/modules/create_invoice/header.ex @@ -0,0 +1,88 @@ +defmodule ExSzamlazzHu.CreateInvoice.Header do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + @enforce_keys [ + :teljesitesDatum, + :fizetesiHataridoDatum, + :fizmod, + :penznem, + :szamlaNyelve + ] + defstruct [ + :keltDatum, + :teljesitesDatum, + :fizetesiHataridoDatum, + :fizmod, + :penznem, + :szamlaNyelve, + :megjegyzes, + :arfolyamBank, + :arfolyam, + :rendelesSzam, + :dijbekeroSzamlaszam, + :elolegszamla, + :vegszamla, + :elolegSzamlaszam, + :helyesbitoszamla, + :helyesbitettSzamlaszam, + :dijbekero, + :szallitolevel, + :logoExtra, + :szamlaszamElotag, + :fizetendoKorrekcio, + :fizetve, + :arresAfa, + :eusAfa, + :szamlaSablon, + :elonezetpdf + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + keltDatum: &"#{&1}", + teljesitesDatum: &"#{&1}", + fizetesiHataridoDatum: &"#{&1}", + fizmod: &"#{&1}", + penznem: &"#{&1}", + szamlaNyelve: &"#{&1}", + megjegyzes: &"#{&1}", + arfolyamBank: &"#{&1}", + arfolyam: &"#{&1}", + rendelesSzam: &"#{&1}", + dijbekeroSzamlaszam: &"#{&1}", + elolegszamla: &"#{&1}", + vegszamla: &"#{&1}", + elolegSzamlaszam: &"#{&1}", + helyesbitoszamla: &"#{&1}", + helyesbitettSzamlaszam: &"#{&1}", + dijbekero: &"#{&1}", + szallitolevel: &"#{&1}", + logoExtra: &"#{&1}", + szamlaszamElotag: &"#{&1}", + fizetendoKorrekcio: &"#{&1}", + fizetve: &"#{&1}", + arresAfa: &"#{&1}", + eusAfa: &"#{&1}", + szamlaSablon: &"#{&1}", + elonezetpdf: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/invoice_data.ex b/lib/modules/create_invoice/invoice_data.ex new file mode 100644 index 0000000..ed9e911 --- /dev/null +++ b/lib/modules/create_invoice/invoice_data.ex @@ -0,0 +1,57 @@ +defmodule ExSzamlazzHu.CreateInvoice.InvoiceData do + @moduledoc false + + alias ExSzamlazzHu.CreateInvoice.Settings + alias ExSzamlazzHu.CreateInvoice.Header + alias ExSzamlazzHu.CreateInvoice.Seller + alias ExSzamlazzHu.CreateInvoice.Customer + alias ExSzamlazzHu.CreateInvoice.Waybill + alias ExSzamlazzHu.CreateInvoice.Items + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + @enforce_keys [:beallitasok, :fejlec, :elado, :vevo, :tetelek] + defstruct [ + :beallitasok, + :fejlec, + :elado, + :vevo, + :fuvarlevel, + :tetelek + ] + + def parse(params) do + %__MODULE__{ + beallitasok: Settings.parse(params[:beallitasok]), + fejlec: Header.parse(params[:fejlec]), + elado: Seller.parse(params[:elado]), + vevo: Customer.parse(params[:vevo]), + fuvarlevel: Waybill.parse(params[:fuvarlevel]), + tetelek: Items.parse(params[:tetelek]) + } + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + beallitasok: &Settings.to_xml(&1), + fejlec: &Header.to_xml(&1), + elado: &Seller.to_xml(&1), + vevo: &Customer.to_xml(&1), + fuvarlevel: &Waybill.to_xml(&1), + tetelek: &Items.to_xml(&1) + ] + + """ + + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/item.ex b/lib/modules/create_invoice/item.ex new file mode 100644 index 0000000..2f1c305 --- /dev/null +++ b/lib/modules/create_invoice/item.ex @@ -0,0 +1,66 @@ +defmodule ExSzamlazzHu.CreateInvoice.Items.Item do + @moduledoc false + + alias ExSzamlazzHu.CreateInvoice.Items.Item.ItemLedger + alias ExSzamlazzHu.Utils.StructToXML + + @type t() :: %__MODULE__{} + + @enforce_keys [ + :megnevezes, + :mennyiseg, + :mennyisegiEgyseg, + :nettoEgysegar, + :afakulcs, + :nettoErtek, + :afaErtek, + :bruttoErtek + ] + defstruct [ + :megnevezes, + :azonosito, + :mennyiseg, + :mennyisegiEgyseg, + :nettoEgysegar, + :afakulcs, + :arresAfaAlap, + :nettoErtek, + :afaErtek, + :bruttoErtek, + :megjegyzes, + :tetelFokonyv + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + __MODULE__ + |> struct(Map.drop(params, [:tetelFokonyv])) + |> Map.put(:tetelFokonyv, ItemLedger.parse(params[:tetelFokonyv])) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + megnevezes: &"#{&1}", + azonosito: &"#{&1}", + mennyiseg: &"#{&1}", + mennyisegiEgyseg: &"#{&1}", + nettoEgysegar: &"#{&1}", + afakulcs: &"#{&1}", + arresAfaAlap: &"#{&1}", + nettoErtek: &"#{&1}", + afaErtek: &"#{&1}", + bruttoErtek: &"#{&1}", + megjegyzes: &"#{&1}", + tetelFokonyv: &ItemLedger.to_xml(&1) + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/item_ledger.ex b/lib/modules/create_invoice/item_ledger.ex new file mode 100644 index 0000000..c828093 --- /dev/null +++ b/lib/modules/create_invoice/item_ledger.ex @@ -0,0 +1,41 @@ +defmodule ExSzamlazzHu.CreateInvoice.Items.Item.ItemLedger do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :gazdasagiEsem, + :gazdasagiEsemAfa, + :arbevetelFokonyviSzam, + :afaFokonyviSzam, + :elszDatumTol, + :elszDatumIg + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + gazdasagiEsem: &"#{&1}", + gazdasagiEsemAfa: &"#{&1}", + arbevetelFokonyviSzam: &"#{&1}", + afaFokonyviSzam: &"#{&1}", + elszDatumTol: &"#{&1}", + elszDatumIg: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/items.ex b/lib/modules/create_invoice/items.ex new file mode 100644 index 0000000..15f1eb9 --- /dev/null +++ b/lib/modules/create_invoice/items.ex @@ -0,0 +1,25 @@ +defmodule ExSzamlazzHu.CreateInvoice.Items do + @moduledoc false + + alias ExSzamlazzHu.CreateInvoice.Items.Item + + def parse(nil), do: [] + + def parse(params) do + Enum.map(params, &Item.parse/1) + end + + def to_xml(items) do + rendered_items = + items + |> Enum.map_join("\n", &Item.to_xml/1) + |> String.replace("\n\n", "\n") + |> String.trim() + + """ + + #{rendered_items} + + """ + end +end diff --git a/lib/modules/create_invoice/mpl.ex b/lib/modules/create_invoice/mpl.ex new file mode 100644 index 0000000..a85b6c9 --- /dev/null +++ b/lib/modules/create_invoice/mpl.ex @@ -0,0 +1,39 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.MPL do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :vevokod, + :vonalkod, + :tomeg, + :kulonszolgaltatasok, + :erteknyilvanitas + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + vevokod: &"#{&1}", + vonalkod: &"#{&1}", + tomeg: &"#{&1}", + kulonszolgaltatasok: &"#{&1}", + erteknyilvanitas: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/ppp.ex b/lib/modules/create_invoice/ppp.ex new file mode 100644 index 0000000..855f41a --- /dev/null +++ b/lib/modules/create_invoice/ppp.ex @@ -0,0 +1,33 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.PPP do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :vonalkodPrefix, + :vonalkodPostfix + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + vonalkodPrefix: &"#{&1}", + vonalkodPostfix: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/result.ex b/lib/modules/create_invoice/result.ex new file mode 100644 index 0000000..1d131d3 --- /dev/null +++ b/lib/modules/create_invoice/result.ex @@ -0,0 +1,23 @@ +defmodule ExSzamlazzHu.CreateInvoice.Result do + @moduledoc """ + Represents the result of the invoice creation process. + + See the [documentation](https://hexdocs.pm/ex_szamlazz_hu/ExSzamlazzHu.html) + """ + @moduledoc since: "0.1.0" + + @type t() :: %__MODULE__{} + + defstruct success: nil, + raw_response: nil, + szlahu_id: nil, + szlahu_szamlaszam: nil, + szlahu_nettovegosszeg: nil, + szlahu_bruttovegosszeg: nil, + szlahu_kintlevoseg: nil, + szlahu_vevoifiokurl: nil, + path_to_pdf_invoice: nil, + szlahu_error: nil, + szlahu_error_code: nil, + szlahu_down: false +end diff --git a/lib/modules/create_invoice/seller.ex b/lib/modules/create_invoice/seller.ex new file mode 100644 index 0000000..ce63448 --- /dev/null +++ b/lib/modules/create_invoice/seller.ex @@ -0,0 +1,41 @@ +defmodule ExSzamlazzHu.CreateInvoice.Seller do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :bank, + :bankszamlaszam, + :emailReplyto, + :emailTargy, + :emailSzoveg, + :alairoNeve + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + bank: &"#{&1}", + bankszamlaszam: &"#{&1}", + emailReplyto: &"#{&1}", + emailTargy: &"#{&1}", + emailSzoveg: &"#{&1}", + alairoNeve: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/settings.ex b/lib/modules/create_invoice/settings.ex new file mode 100644 index 0000000..e3672be --- /dev/null +++ b/lib/modules/create_invoice/settings.ex @@ -0,0 +1,50 @@ +defmodule ExSzamlazzHu.CreateInvoice.Settings do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + @enforce_keys [:eszamla, :szamlaLetoltes] + defstruct felhasznalo: nil, + jelszo: nil, + szamlaagentkulcs: nil, + eszamla: nil, + szamlaLetoltes: nil, + szamlaLetoltesPld: nil, + valaszVerzio: 1, + aggregator: nil, + guardian: nil, + cikkazoninvoice: nil, + szamlaKulsoAzon: nil + + @spec parse(map()) :: t() + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + felhasznalo: &"#{&1}", + jelszo: &"#{&1}", + szamlaagentkulcs: &"#{&1}", + eszamla: &"#{&1}", + szamlaLetoltes: &"#{&1}", + szamlaLetoltesPld: &"#{&1}", + valaszVerzio: &"#{&1}", + aggregator: &"#{&1}", + guardian: &"#{&1}", + cikkazoninvoice: &"#{&1}", + szamlaKulsoAzon: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/sprinter.ex b/lib/modules/create_invoice/sprinter.ex new file mode 100644 index 0000000..5c0b687 --- /dev/null +++ b/lib/modules/create_invoice/sprinter.ex @@ -0,0 +1,41 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.Sprinter do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :azonosito, + :feladokod, + :iranykod, + :csomagszam, + :vonalkodPostfix, + :szallitasiIdo + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + azonosito: &"#{&1}", + feladokod: &"#{&1}", + iranykod: &"#{&1}", + csomagszam: &"#{&1}", + vonalkodPostfix: &"#{&1}", + szallitasiIdo: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/transoflex.ex b/lib/modules/create_invoice/transoflex.ex new file mode 100644 index 0000000..e0598de --- /dev/null +++ b/lib/modules/create_invoice/transoflex.ex @@ -0,0 +1,41 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.Transoflex do + @moduledoc false + + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :azonosito, + :shipmentID, + :csomagszam, + :countryCode, + :zip, + :service + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + struct(__MODULE__, params) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + azonosito: &"#{&1}", + shipmentID: &"#{&1}", + csomagszam: &"#{&1}", + countryCode: &"#{&1}", + zip: &"#{&1}", + service: &"#{&1}" + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/modules/create_invoice/waybill.ex b/lib/modules/create_invoice/waybill.ex new file mode 100644 index 0000000..d06932b --- /dev/null +++ b/lib/modules/create_invoice/waybill.ex @@ -0,0 +1,54 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill do + @moduledoc false + + alias ExSzamlazzHu.CreateInvoice.Waybill.Transoflex + alias ExSzamlazzHu.CreateInvoice.Waybill.Sprinter + alias ExSzamlazzHu.CreateInvoice.Waybill.PPP + alias ExSzamlazzHu.CreateInvoice.Waybill.MPL + alias ExSzamlazzHu.Utils.StructToXML + + @type t :: %__MODULE__{} + + defstruct [ + :uticel, + :futarSzolgalat, + :vonalkod, + :megjegyzes, + :tof, + :ppp, + :sprinter, + :mpl + ] + + @spec parse(map() | nil) :: t() | nil + def parse(nil), do: nil + + def parse(params) do + __MODULE__ + |> struct(Map.drop(params, [:tof, :ppp, :sprinter, :mpl])) + |> Map.put(:tof, Transoflex.parse(params[:tof])) + |> Map.put(:ppp, PPP.parse(params[:ppp])) + |> Map.put(:sprinter, Sprinter.parse(params[:sprinter])) + |> Map.put(:mpl, MPL.parse(params[:mpl])) + end + + @spec to_xml(t()) :: String.t() + def to_xml(%__MODULE__{} = module) do + tags = [ + uticel: &"#{&1}", + futarSzolgalat: &"#{&1}", + vonalkod: &"#{&1}", + megjegyzes: &"#{&1}", + tof: &Transoflex.to_xml(&1), + ppp: &PPP.to_xml(&1), + sprinter: &Sprinter.to_xml(&1), + mpl: &MPL.to_xml(&1) + ] + + """ + + #{StructToXML.run(module, tags)} + + """ + end +end diff --git a/lib/utils/struct_to_xml.ex b/lib/utils/struct_to_xml.ex new file mode 100644 index 0000000..cade68e --- /dev/null +++ b/lib/utils/struct_to_xml.ex @@ -0,0 +1,22 @@ +defmodule ExSzamlazzHu.Utils.StructToXML do + @moduledoc false + + @type tag_converter_param :: {atom(), (any() -> String.t())} + + @spec run(struct(), params :: [tag_converter_param]) :: String.t() + def run(struct, params) do + params + |> Enum.map(&apply_xml_producing_funs(&1, struct)) + |> Enum.reject(&is_nil/1) + |> Enum.join("\n") + |> String.replace("\n\n", "\n") + |> String.trim() + end + + defp apply_xml_producing_funs({tag, fun}, struct) do + case Map.get(struct, tag) do + nil -> nil + value -> fun.(value) + end + end +end diff --git a/lib/utils/temporary_file.ex b/lib/utils/temporary_file.ex new file mode 100644 index 0000000..3944c01 --- /dev/null +++ b/lib/utils/temporary_file.ex @@ -0,0 +1,16 @@ +defmodule ExSzamlazzHu.Utils.TemporaryFile do + @moduledoc false + + @spec save(filename :: String.t(), content :: String.t()) :: + {:ok, String.t()} | {:error, :cannot_save_temporary_file} + def save(filename, content) do + with dir when not is_nil(dir) <- System.tmp_dir(), + path <- Path.join(dir, filename), + :ok <- File.write(path, content) do + {:ok, path} + else + nil -> {:error, :cannot_save_temporary_file} + {:error, _posix} -> {:error, :cannot_save_temporary_file} + end + end +end diff --git a/mix.exs b/mix.exs index 14c7d14..0088c13 100644 --- a/mix.exs +++ b/mix.exs @@ -12,6 +12,7 @@ defmodule ExSzamlazzHu.MixProject do name: "ExSzamlazzHu", source_url: @source_url, elixir: "~> 1.14", + elixirc_paths: elixirc_paths(Mix.env()), build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, package: package(), @@ -31,10 +32,12 @@ defmodule ExSzamlazzHu.MixProject do defp deps do [ - {:tesla, "~> 1.7.0"}, + {:tesla, "== 1.8.0"}, + {:hackney, "== 1.20.1", only: [:dev, :test]}, {:ex_doc, "~> 0.29.4", only: :dev}, {:credo, "~> 1.6", only: [:dev, :test], runtime: false}, - {:dialyxir, "~> 1.3", only: [:dev, :test], runtime: false} + {:dialyxir, "~> 1.3", only: [:dev, :test], runtime: false}, + {:faker, "~> 0.17", only: [:test]} ] end @@ -44,6 +47,9 @@ defmodule ExSzamlazzHu.MixProject do """ end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] + defp package do [ files: ["lib", "mix.exs", "README*", "LICENSE*"], diff --git a/mix.lock b/mix.lock index 5208788..d263890 100644 --- a/mix.lock +++ b/mix.lock @@ -1,16 +1,25 @@ %{ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"}, "dialyxir": {:hex, :dialyxir, "1.3.0", "fd1672f0922b7648ff9ce7b1b26fcf0ef56dda964a459892ad15f6b4410b5284", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "00b2a4bcd6aa8db9dcb0b38c1225b7277dca9bc370b6438715667071a304696f"}, "earmark_parser": {:hex, :earmark_parser, "1.4.32", "fa739a0ecfa34493de19426681b23f6814573faee95dfd4b4aafe15a7b5b32c6", [:mix], [], "hexpm", "b8b0dd77d60373e77a3d7e8afa598f325e49e8663a51bcc2b88ef41838cca755"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"}, + "faker": {:hex, :faker, "0.17.0", "671019d0652f63aefd8723b72167ecdb284baf7d47ad3a82a15e9b8a6df5d1fa", [:mix], [], "hexpm", "a7d4ad84a93fd25c5f5303510753789fc2433ff241bf3b4144d3f6f291658a6a"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, + "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.2", "ad87296a092a46e03b7e9b0be7631ddcf64c790fa68a9ef5323b6cbb36affc72", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f3f5a1ca93ce6e092d92b6d9c049bcda58a3b617a8d888f8e7231c85630e8108"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "nimble_parsec": {:hex, :nimble_parsec, "1.3.1", "2c54013ecf170e249e9291ed0a62e5832f70a476c61da16f6aac6dca0189f2af", [:mix], [], "hexpm", "2682e3c0b2eb58d90c6375fc0cc30bc7be06f365bf72608804fb9cffa5e1b167"}, - "tesla": {:hex, :tesla, "1.7.0", "a62dda2f80d4f8a925eb7b8c5b78c461e0eb996672719fe1a63b26321a5f8b4e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2e64f01ebfdb026209b47bc651a0e65203fcff4ae79c11efb73c4852b00dc313"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, + "tesla": {:hex, :tesla, "1.8.0", "d511a4f5c5e42538d97eef7c40ec4f3e44effdc5068206f42ed859e09e51d1fd", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, ">= 1.0.0", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "10501f360cd926a309501287470372af1a6e1cbed0f43949203a4c13300bc79f"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, } diff --git a/test/ex_szamlazz_hu_test.exs b/test/ex_szamlazz_hu_test.exs index 3f9d657..6ac939a 100644 --- a/test/ex_szamlazz_hu_test.exs +++ b/test/ex_szamlazz_hu_test.exs @@ -1,9 +1,8 @@ defmodule ExSzamlazzHuTest do - use ExUnit.Case + use ExUnit.Case, async: true doctest ExSzamlazzHu test "greets the world" do - assert ExSzamlazzHu.create_invoice(%{}) == {:error, :not_implemented} assert ExSzamlazzHu.reverse_invoice(%{}) == {:error, :not_implemented} assert ExSzamlazzHu.register_credit_entry(%{}) == {:error, :not_implemented} assert ExSzamlazzHu.query_invoice_pdf(%{}) == {:error, :not_implemented} diff --git a/test/lib/modules/create_invoice/customer_ledger_test.exs b/test/lib/modules/create_invoice/customer_ledger_test.exs new file mode 100644 index 0000000..e6bdbf2 --- /dev/null +++ b/test/lib/modules/create_invoice/customer_ledger_test.exs @@ -0,0 +1,44 @@ +defmodule ExSzamlazzHu.CreateInvoice.CustomerLedgerTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.CustomerLedger + alias ExSzamlazzHu.Factories.CustomerLedgerFactory + + describe "parse/1" do + test "should parse a valid CustomerLedger" do + assert CustomerLedger.parse(params()) == %CustomerLedger{ + konyvelesDatum: "accounting_date", + vevoAzonosito: "customer_identifier", + vevoFokonyviSzam: "ledger_number", + folyamatosTelj: "continuous_delivery", + elszDatumTol: "settlement_date_from", + elszDatumIg: "settlement_date_to" + } + end + + test "should parse an empty CustomerLedger" do + assert CustomerLedger.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert params() + |> CustomerLedger.parse() + |> CustomerLedger.to_xml() == + """ + + accounting_date + customer_identifier + ledger_number + continuous_delivery + settlement_date_from + settlement_date_to + + """ + end + end + + def params() do + CustomerLedgerFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/customer_test.exs b/test/lib/modules/create_invoice/customer_test.exs new file mode 100644 index 0000000..e755f3d --- /dev/null +++ b/test/lib/modules/create_invoice/customer_test.exs @@ -0,0 +1,83 @@ +defmodule ExSzamlazzHu.CreateInvoice.CustomerTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Customer + alias ExSzamlazzHu.Factories.CustomerFactory + alias ExSzamlazzHu.Factories.CustomerLedgerFactory + + describe "parse/1" do + test "should parse a valid Customer" do + assert Customer.parse(params()) == %Customer{ + nev: "name", + orszag: "country", + irsz: "zip_code", + telepules: "city", + cim: "address", + email: "email", + sendEmail: false, + adoalany: 123, + adoszam: "tax_identifier", + csoportazonosito: "group_identifier", + adoszamEU: "eu_tax_identifier", + postazasiNev: "mailing_name", + postazasiOrszag: "mailing_country", + postazasiIrsz: "mailing_zip", + postazasiTelepules: "mailing_city", + postazasiCim: "mailing_address", + vevoFokonyv: nil, + azonosito: "identifier", + alairoNeve: "name_of_signatory", + telefonszam: "phone_number", + megjegyzes: "comment" + } + end + + test "should parse an empty Customer" do + assert Customer.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert %{vevoFokonyv: CustomerLedgerFactory.get_params()} + |> params() + |> Customer.parse() + |> Customer.to_xml() == + """ + + name + country + zip_code + city + address + email + false + 123 + tax_identifier + group_identifier + eu_tax_identifier + mailing_name + mailing_country + mailing_zip + mailing_city + mailing_address + + accounting_date + customer_identifier + ledger_number + continuous_delivery + settlement_date_from + settlement_date_to + + identifier + name_of_signatory + phone_number + comment + + """ + end + end + + def params(params \\ %{nope: "nope"}) do + CustomerFactory.get_params(params) + end +end diff --git a/test/lib/modules/create_invoice/header_test.exs b/test/lib/modules/create_invoice/header_test.exs new file mode 100644 index 0000000..a535e0c --- /dev/null +++ b/test/lib/modules/create_invoice/header_test.exs @@ -0,0 +1,88 @@ +defmodule ExSzamlazzHu.CreateInvoice.HeaderTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Header + alias ExSzamlazzHu.Factories.HeaderFactory + + describe "parse/1" do + test "should parse a valid Header" do + assert Header.parse(params()) == %Header{ + keltDatum: "1997-08-29", + teljesitesDatum: "1997-08-29", + fizetesiHataridoDatum: "1997-08-29", + fizmod: "payment_method", + penznem: "HUF", + szamlaNyelve: "en", + megjegyzes: "comment", + arfolyamBank: "exchange_rate_bank", + arfolyam: "123.4", + rendelesSzam: "order_identifier", + dijbekeroSzamlaszam: "fee_request_identifier", + elolegszamla: false, + vegszamla: true, + elolegSzamlaszam: nil, + helyesbitoszamla: false, + helyesbitettSzamlaszam: nil, + dijbekero: false, + szallitolevel: false, + logoExtra: "logo_extra", + szamlaszamElotag: "PRFX", + fizetendoKorrekcio: "123.4", + fizetve: true, + arresAfa: false, + eusAfa: false, + szamlaSablon: "invoice_draft", + elonezetpdf: false + } + end + + test "should parse an empty Header" do + assert Header.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert %{ + elolegSzamlaszam: "proforma_invoice_identifier", + helyesbitettSzamlaszam: "corrected_invoice_identifier" + } + |> params() + |> Header.parse() + |> Header.to_xml() == + """ + + 1997-08-29 + 1997-08-29 + 1997-08-29 + payment_method + HUF + en + comment + exchange_rate_bank + 123.4 + order_identifier + fee_request_identifier + false + true + proforma_invoice_identifier + false + corrected_invoice_identifier + false + false + logo_extra + PRFX + 123.4 + true + false + false + invoice_draft + false + + """ + end + end + + def params(params \\ %{nope: "nope"}) do + HeaderFactory.get_params(params) + end +end diff --git a/test/lib/modules/create_invoice/invoice_data_test.exs b/test/lib/modules/create_invoice/invoice_data_test.exs new file mode 100644 index 0000000..1851361 --- /dev/null +++ b/test/lib/modules/create_invoice/invoice_data_test.exs @@ -0,0 +1,282 @@ +defmodule ExSzamlazzHu.CreateInvoice.InvoiceDataTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.InvoiceData + alias ExSzamlazzHu.CreateInvoice.Settings + alias ExSzamlazzHu.CreateInvoice.Header + alias ExSzamlazzHu.CreateInvoice.Customer + alias ExSzamlazzHu.CreateInvoice.Seller + alias ExSzamlazzHu.CreateInvoice.Waybill + alias ExSzamlazzHu.CreateInvoice.Waybill.MPL + alias ExSzamlazzHu.CreateInvoice.Items.Item + alias ExSzamlazzHu.Factories.InvoiceDataFactory + alias ExSzamlazzHu.Factories.HeaderFactory + alias ExSzamlazzHu.Factories.WaybillFactory + alias ExSzamlazzHu.Factories.CustomerFactory + alias ExSzamlazzHu.Factories.CustomerLedgerFactory + alias ExSzamlazzHu.Factories.ItemFactory + alias ExSzamlazzHu.Factories.ItemLedgerFactory + + describe "parse/1" do + test "should parse a valid InvoiceData" do + assert %{fuvarlevel: WaybillFactory.get_params(%{mpl: %{}})} + |> params() + |> InvoiceData.parse() == + %InvoiceData{ + beallitasok: %Settings{ + felhasznalo: "user", + jelszo: "password", + szamlaagentkulcs: "agent_key", + eszamla: true, + szamlaLetoltes: false, + szamlaLetoltesPld: 0, + valaszVerzio: 1, + aggregator: "aggregator", + guardian: false, + cikkazoninvoice: false, + szamlaKulsoAzon: "external_invoice_identifier" + }, + fejlec: %Header{ + keltDatum: "1997-08-29", + teljesitesDatum: "1997-08-29", + fizetesiHataridoDatum: "1997-08-29", + fizmod: "payment_method", + penznem: "HUF", + szamlaNyelve: "en", + megjegyzes: "comment", + arfolyamBank: "exchange_rate_bank", + arfolyam: "123.4", + rendelesSzam: "order_identifier", + dijbekeroSzamlaszam: "fee_request_identifier", + elolegszamla: false, + vegszamla: true, + elolegSzamlaszam: nil, + helyesbitoszamla: false, + helyesbitettSzamlaszam: nil, + dijbekero: false, + szallitolevel: false, + logoExtra: "logo_extra", + szamlaszamElotag: "PRFX", + fizetendoKorrekcio: "123.4", + fizetve: true, + arresAfa: false, + eusAfa: false, + szamlaSablon: "invoice_draft", + elonezetpdf: false + }, + vevo: %Customer{ + nev: "name", + orszag: "country", + irsz: "zip_code", + telepules: "city", + cim: "address", + email: "email", + sendEmail: false, + adoalany: 123, + adoszam: "tax_identifier", + csoportazonosito: "group_identifier", + adoszamEU: "eu_tax_identifier", + postazasiNev: "mailing_name", + postazasiOrszag: "mailing_country", + postazasiIrsz: "mailing_zip", + postazasiTelepules: "mailing_city", + postazasiCim: "mailing_address", + vevoFokonyv: nil, + azonosito: "identifier", + alairoNeve: "name_of_signatory", + telefonszam: "phone_number", + megjegyzes: "comment" + }, + elado: %Seller{ + bank: "bank", + bankszamlaszam: "bank_account_number", + emailReplyto: "email_reply_to", + emailTargy: "email_subject", + emailSzoveg: "email_text", + alairoNeve: "name_of_signatory" + }, + fuvarlevel: %Waybill{ + uticel: "destination", + futarSzolgalat: "three_pl_provider", + vonalkod: "barcode", + megjegyzes: "comment", + tof: nil, + ppp: nil, + sprinter: nil, + mpl: %MPL{ + vevokod: "buyer_identifier", + vonalkod: "barcode", + tomeg: "weight", + kulonszolgaltatasok: "extra_services", + erteknyilvanitas: "value_statement" + } + }, + tetelek: [ + %Item{ + megnevezes: "name", + azonosito: "identifier", + mennyiseg: "1.5", + mennyisegiEgyseg: "unit", + nettoEgysegar: "net_unit_price", + afakulcs: "27", + arresAfaAlap: "margin_vat_base", + nettoErtek: "100", + afaErtek: "27", + bruttoErtek: "127", + megjegyzes: "comment", + tetelFokonyv: nil + } + ] + } + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert %{ + fejlec: + HeaderFactory.get_params(%{ + elolegSzamlaszam: "proforma_invoice_identifier", + helyesbitettSzamlaszam: "corrected_invoice_identifier" + }), + vevo: CustomerFactory.get_params(%{vevoFokonyv: CustomerLedgerFactory.get_params()}), + fuvarlevel: WaybillFactory.get_params(%{mpl: %{}}), + tetelek: [ + ItemFactory.get_params(%{ + tetelFokonyv: ItemLedgerFactory.get_params() + }) + ] + } + |> params() + |> InvoiceData.parse() + |> InvoiceData.to_xml() == + """ + + + + user + password + agent_key + true + false + 0 + 1 + aggregator + false + false + external_invoice_identifier + + + 1997-08-29 + 1997-08-29 + 1997-08-29 + payment_method + HUF + en + comment + exchange_rate_bank + 123.4 + order_identifier + fee_request_identifier + false + true + proforma_invoice_identifier + false + corrected_invoice_identifier + false + false + logo_extra + PRFX + 123.4 + true + false + false + invoice_draft + false + + + bank + bank_account_number + email_reply_to + email_subject + email_text + name_of_signatory + + + name + country + zip_code + city + address + email + false + 123 + tax_identifier + group_identifier + eu_tax_identifier + mailing_name + mailing_country + mailing_zip + mailing_city + mailing_address + + accounting_date + customer_identifier + ledger_number + continuous_delivery + settlement_date_from + settlement_date_to + + identifier + name_of_signatory + phone_number + comment + + + destination + three_pl_provider + barcode + comment + + buyer_identifier + barcode + weight + extra_services + value_statement + + + + + name + identifier + 1.5 + unit + net_unit_price + 27 + margin_vat_base + 100 + 27 + 127 + comment + + economic_event + economic_event_vat + income_ledger_number + vat_ledger_number + settlement_date_from + settlement_date_to + + + + + """ + end + end + + def params(params \\ %{nope: "nope"}) do + InvoiceDataFactory.get_params(params) + end +end diff --git a/test/lib/modules/create_invoice/item_ledger_test.exs b/test/lib/modules/create_invoice/item_ledger_test.exs new file mode 100644 index 0000000..9886d15 --- /dev/null +++ b/test/lib/modules/create_invoice/item_ledger_test.exs @@ -0,0 +1,44 @@ +defmodule ExSzamlazzHu.CreateInvoice.ItemLedgerTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Items.Item.ItemLedger + alias ExSzamlazzHu.Factories.ItemLedgerFactory + + describe "parse/1" do + test "should parse a valid ItemLedger" do + assert ItemLedger.parse(params()) == %ItemLedger{ + gazdasagiEsem: "economic_event", + gazdasagiEsemAfa: "economic_event_vat", + arbevetelFokonyviSzam: "income_ledger_number", + afaFokonyviSzam: "vat_ledger_number", + elszDatumTol: "settlement_date_from", + elszDatumIg: "settlement_date_to" + } + end + + test "should parse an empty ItemLedger" do + assert ItemLedger.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert params() + |> ItemLedger.parse() + |> ItemLedger.to_xml() == + """ + + economic_event + economic_event_vat + income_ledger_number + vat_ledger_number + settlement_date_from + settlement_date_to + + """ + end + end + + def params() do + ItemLedgerFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/item_test.exs b/test/lib/modules/create_invoice/item_test.exs new file mode 100644 index 0000000..aea1ee9 --- /dev/null +++ b/test/lib/modules/create_invoice/item_test.exs @@ -0,0 +1,65 @@ +defmodule ExSzamlazzHu.CreateInvoice.ItemTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Items.Item + alias ExSzamlazzHu.Factories.ItemFactory + alias ExSzamlazzHu.Factories.ItemLedgerFactory + + describe "parse/1" do + test "should parse a valid Item" do + assert Item.parse(params()) == %Item{ + megnevezes: "name", + azonosito: "identifier", + mennyiseg: "1.5", + mennyisegiEgyseg: "unit", + nettoEgysegar: "net_unit_price", + afakulcs: "27", + arresAfaAlap: "margin_vat_base", + nettoErtek: "100", + afaErtek: "27", + bruttoErtek: "127", + megjegyzes: "comment", + tetelFokonyv: nil + } + end + + test "should parse an empty Item" do + assert Item.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert %{tetelFokonyv: ItemLedgerFactory.get_params()} + |> params() + |> Item.parse() + |> Item.to_xml() == + """ + + name + identifier + 1.5 + unit + net_unit_price + 27 + margin_vat_base + 100 + 27 + 127 + comment + + economic_event + economic_event_vat + income_ledger_number + vat_ledger_number + settlement_date_from + settlement_date_to + + + """ + end + end + + def params(params \\ %{nope: "nope"}) do + ItemFactory.get_params(params) + end +end diff --git a/test/lib/modules/create_invoice/items_test.exs b/test/lib/modules/create_invoice/items_test.exs new file mode 100644 index 0000000..700ceb5 --- /dev/null +++ b/test/lib/modules/create_invoice/items_test.exs @@ -0,0 +1,70 @@ +defmodule ExSzamlazzHu.CreateInvoice.ItemsTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Items + alias ExSzamlazzHu.CreateInvoice.Items.Item + alias ExSzamlazzHu.Factories.ItemFactory + alias ExSzamlazzHu.Factories.ItemLedgerFactory + + describe "parse/1" do + test "should parse a valid Item list" do + assert Items.parse(params()) == [ + %Item{ + megnevezes: "name", + azonosito: "identifier", + mennyiseg: "1.5", + mennyisegiEgyseg: "unit", + nettoEgysegar: "net_unit_price", + afakulcs: "27", + arresAfaAlap: "margin_vat_base", + nettoErtek: "100", + afaErtek: "27", + bruttoErtek: "127", + megjegyzes: "comment", + tetelFokonyv: nil + } + ] + end + + test "should parse an empty Item list" do + assert Items.parse(nil) == [] + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert %{tetelFokonyv: ItemLedgerFactory.get_params()} + |> params() + |> Items.parse() + |> Items.to_xml() == + """ + + + name + identifier + 1.5 + unit + net_unit_price + 27 + margin_vat_base + 100 + 27 + 127 + comment + + economic_event + economic_event_vat + income_ledger_number + vat_ledger_number + settlement_date_from + settlement_date_to + + + + """ + end + end + + defp params(params \\ %{}) do + [ItemFactory.get_params(params)] + end +end diff --git a/test/lib/modules/create_invoice/mpl_test.exs b/test/lib/modules/create_invoice/mpl_test.exs new file mode 100644 index 0000000..10e2a8a --- /dev/null +++ b/test/lib/modules/create_invoice/mpl_test.exs @@ -0,0 +1,42 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.MPLTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Waybill.MPL + alias ExSzamlazzHu.Factories.MPLFactory + + describe "parse/1" do + test "should parse a valid waybill for MPL" do + assert MPL.parse(params()) == %MPL{ + vevokod: "buyer_identifier", + vonalkod: "barcode", + tomeg: "weight", + kulonszolgaltatasok: "extra_services", + erteknyilvanitas: "value_statement" + } + end + + test "should parse an empty waybill for MPL" do + assert MPL.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should parse a valid waybill for MPL" do + assert params() + |> MPL.parse() + |> MPL.to_xml() == + """ + + buyer_identifier + barcode + weight + extra_services + value_statement + + """ + end + end + + def params() do + MPLFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/ppp_test.exs b/test/lib/modules/create_invoice/ppp_test.exs new file mode 100644 index 0000000..082db13 --- /dev/null +++ b/test/lib/modules/create_invoice/ppp_test.exs @@ -0,0 +1,36 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.PPPTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Waybill.PPP + alias ExSzamlazzHu.Factories.PPPFactory + + describe "parse/1" do + test "should parse a valid waybill for PPP" do + assert PPP.parse(params()) == %PPP{ + vonalkodPrefix: "barcode_prefix", + vonalkodPostfix: "barcode_postfix" + } + end + + test "should parse an empty waybill for PPP" do + assert PPP.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert params() + |> PPP.parse() + |> PPP.to_xml() == + """ + + barcode_prefix + barcode_postfix + + """ + end + end + + def params() do + PPPFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/seller_test.exs b/test/lib/modules/create_invoice/seller_test.exs new file mode 100644 index 0000000..2e848a5 --- /dev/null +++ b/test/lib/modules/create_invoice/seller_test.exs @@ -0,0 +1,44 @@ +defmodule ExSzamlazzHu.CreateInvoice.SellerTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Seller + alias ExSzamlazzHu.Factories.SellerFactory + + describe "parse/1" do + test "should parse a valid Seller" do + assert Seller.parse(params()) == %Seller{ + bank: "bank", + bankszamlaszam: "bank_account_number", + emailReplyto: "email_reply_to", + emailTargy: "email_subject", + emailSzoveg: "email_text", + alairoNeve: "name_of_signatory" + } + end + + test "should parse an empty Seller" do + assert Seller.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert params() + |> Seller.parse() + |> Seller.to_xml() == + """ + + bank + bank_account_number + email_reply_to + email_subject + email_text + name_of_signatory + + """ + end + end + + def params() do + SellerFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/settings_test.exs b/test/lib/modules/create_invoice/settings_test.exs new file mode 100644 index 0000000..9d00975 --- /dev/null +++ b/test/lib/modules/create_invoice/settings_test.exs @@ -0,0 +1,54 @@ +defmodule ExSzamlazzHu.CreateInvoice.SettingsTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Settings + alias ExSzamlazzHu.Factories.SettingsFactory + + describe "parse/1" do + test "should parse a valid Settings" do + assert Settings.parse(params()) == %Settings{ + felhasznalo: "user", + jelszo: "password", + szamlaagentkulcs: "agent_key", + eszamla: true, + szamlaLetoltes: false, + szamlaLetoltesPld: 0, + valaszVerzio: 1, + aggregator: "aggregator", + guardian: false, + cikkazoninvoice: false, + szamlaKulsoAzon: "external_invoice_identifier" + } + end + + test "should parse an empty Settings" do + assert Settings.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert params() + |> Settings.parse() + |> Settings.to_xml() == + """ + + user + password + agent_key + true + false + 0 + 1 + aggregator + false + false + external_invoice_identifier + + """ + end + end + + def params() do + SettingsFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/sprinter_test.exs b/test/lib/modules/create_invoice/sprinter_test.exs new file mode 100644 index 0000000..4bb9664 --- /dev/null +++ b/test/lib/modules/create_invoice/sprinter_test.exs @@ -0,0 +1,44 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.SprinterTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Waybill.Sprinter + alias ExSzamlazzHu.Factories.SprinterFactory + + describe "parse/1" do + test "should parse a valid waybill for Sprinter" do + assert Sprinter.parse(params()) == %Sprinter{ + azonosito: "identifier", + feladokod: "sender_identifier", + iranykod: "code_of_direction", + csomagszam: "package_identifier", + vonalkodPostfix: "barcode_postfix", + szallitasiIdo: "delivery_deadline" + } + end + + test "should parse an empty waybill for Sprinter" do + assert Sprinter.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert params() + |> Sprinter.parse() + |> Sprinter.to_xml() == + """ + + identifier + sender_identifier + code_of_direction + package_identifier + barcode_postfix + delivery_deadline + + """ + end + end + + def params() do + SprinterFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/transoflex_test.exs b/test/lib/modules/create_invoice/transoflex_test.exs new file mode 100644 index 0000000..70b1e49 --- /dev/null +++ b/test/lib/modules/create_invoice/transoflex_test.exs @@ -0,0 +1,44 @@ +defmodule ExSzamlazzHu.CreateInvoice.Waybill.TransoflexTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Waybill.Transoflex + alias ExSzamlazzHu.Factories.TransoflexFactory + + describe "parse/1" do + test "should parse a valid waybill for Transoflex" do + assert Transoflex.parse(params()) == %Transoflex{ + azonosito: "identifier", + shipmentID: "shipment_id", + csomagszam: "package_identifier", + countryCode: "country_code", + zip: "zip", + service: "service" + } + end + + test "should parse an empty waybill for Transoflex" do + assert Transoflex.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert params() + |> Transoflex.parse() + |> Transoflex.to_xml() == + """ + + identifier + shipment_id + package_identifier + country_code + zip + service + + """ + end + end + + def params() do + TransoflexFactory.get_params(%{nope: "nope"}) + end +end diff --git a/test/lib/modules/create_invoice/waybill_test.exs b/test/lib/modules/create_invoice/waybill_test.exs new file mode 100644 index 0000000..2e98040 --- /dev/null +++ b/test/lib/modules/create_invoice/waybill_test.exs @@ -0,0 +1,79 @@ +defmodule ExSzamlazzHu.CreateInvoice.WaybillTest do + use ExUnit.Case, async: true + alias ExSzamlazzHu.CreateInvoice.Waybill + alias ExSzamlazzHu.CreateInvoice.Waybill.MPL + alias ExSzamlazzHu.Factories.WaybillFactory + + describe "parse/1" do + test "should parse a valid waybill" do + assert Waybill.parse(params(%{mpl: %{}})) == %Waybill{ + uticel: "destination", + futarSzolgalat: "three_pl_provider", + vonalkod: "barcode", + megjegyzes: "comment", + tof: nil, + ppp: nil, + sprinter: nil, + mpl: %MPL{ + vevokod: "buyer_identifier", + vonalkod: "barcode", + tomeg: "weight", + kulonszolgaltatasok: "extra_services", + erteknyilvanitas: "value_statement" + } + } + end + + test "should parse an empty waybill" do + assert Waybill.parse(nil) == nil + end + end + + describe "to_xml/1" do + test "should return a valid XML" do + assert %{tof: %{}, ppp: %{}, sprinter: %{}, mpl: %{}} + |> params() + |> Waybill.parse() + |> Waybill.to_xml() == + """ + + destination + three_pl_provider + barcode + comment + + identifier + shipment_id + package_identifier + country_code + zip + service + + + barcode_prefix + barcode_postfix + + + identifier + sender_identifier + code_of_direction + package_identifier + barcode_postfix + delivery_deadline + + + buyer_identifier + barcode + weight + extra_services + value_statement + + + """ + end + end + + def params(params \\ %{nope: "nope", mpl: %{}}) do + WaybillFactory.get_params(params) + end +end diff --git a/test/lib/modules/create_invoice_test.exs b/test/lib/modules/create_invoice_test.exs new file mode 100644 index 0000000..6cffe03 --- /dev/null +++ b/test/lib/modules/create_invoice_test.exs @@ -0,0 +1,136 @@ +defmodule ExSzamlazzHu.CreateInvoiceTest do + use ExUnit.Case, async: false + alias ExSzamlazzHu.CreateInvoice + + @moduletag :external + + describe "run/1" do + test "should successfully create an invoice and save the invoice pdf locally when the response type is text" do + params = + params([ + {[:beallitasok, :valaszVerzio], 1}, + {[:beallitasok, :szamlaLetoltes], true} + ]) + + assert {:ok, %CreateInvoice.Result{success: true} = result} = attempt_with_retry(params) + + assert File.exists?(result.path_to_pdf_invoice) + assert %Tesla.Env{} = result.raw_response + assert result.raw_response.body != nil + assert result.szlahu_id != nil + assert result.szlahu_kintlevoseg == "0" + assert result.szlahu_nettovegosszeg == "200" + assert result.szlahu_bruttovegosszeg == "254" + assert result.szlahu_szamlaszam != nil + assert result.szlahu_vevoifiokurl != nil + assert result.szlahu_error == nil + assert result.szlahu_error_code == nil + assert result.szlahu_down == false + end + + test "should successfully create an invoice and save the invoice pdf locally when the response type is xml" do + params = + params([ + {[:beallitasok, :valaszVerzio], 2}, + {[:beallitasok, :szamlaLetoltes], true} + ]) + + assert {:ok, %CreateInvoice.Result{success: true} = result} = attempt_with_retry(params) + + assert File.exists?(result.path_to_pdf_invoice) + assert %Tesla.Env{} = result.raw_response + assert result.raw_response.body =~ " + DateTime.to_iso8601(DateTime.utc_now()) <> + for(_ <- 1..5, into: "", do: <>), + dijbekero: false, + fizetve: true + }, + elado: %{}, + vevo: %{ + nev: Faker.Person.En.name(), + orszag: Faker.Address.country(), + irsz: Faker.Address.postcode(), + telepules: Faker.Address.city(), + cim: Faker.Address.street_address(), + email: Faker.Internet.email(), + sendEmail: false + }, + tetelek: [ + %{ + megnevezes: Faker.Commerce.product_name(), + mennyiseg: 2, + mennyisegiEgyseg: "db", + nettoEgysegar: 100, + afakulcs: 27, + nettoErtek: 200, + afaErtek: 54, + bruttoErtek: 254 + } + ] + } + + [{[:beallitasok, :szamlaagentkulcs], System.fetch_env!("SZAMLAZZ_AGENT_KEY")} | custom_params] + |> Enum.reduce(params, fn {key, value}, acc -> + put_in(acc, key, value) + end) + end + + defp attempt_with_retry(params) do + case CreateInvoice.run(params) do + {:error, :closed} -> CreateInvoice.run(params) + result -> result + end + end +end diff --git a/test/support/factories/customer_factory.ex b/test/support/factories/customer_factory.ex new file mode 100644 index 0000000..49d9dd7 --- /dev/null +++ b/test/support/factories/customer_factory.ex @@ -0,0 +1,39 @@ +defmodule ExSzamlazzHu.Factories.CustomerFactory do + @moduledoc false + + alias ExSzamlazzHu.Factories.CustomerLedgerFactory + + def get_params(params \\ %{}) do + %{ + nev: "name", + orszag: "country", + irsz: "zip_code", + telepules: "city", + cim: "address", + email: "email", + sendEmail: false, + adoalany: 123, + adoszam: "tax_identifier", + csoportazonosito: "group_identifier", + adoszamEU: "eu_tax_identifier", + postazasiNev: "mailing_name", + postazasiOrszag: "mailing_country", + postazasiIrsz: "mailing_zip", + postazasiTelepules: "mailing_city", + postazasiCim: "mailing_address", + vevoFokonyv: nil, + azonosito: "identifier", + alairoNeve: "name_of_signatory", + telefonszam: "phone_number", + megjegyzes: "comment" + } + |> Map.merge(params) + |> maybe_add_customer_ledger() + end + + defp maybe_add_customer_ledger(%{vevoFokonyv: nil} = params), do: params + + defp maybe_add_customer_ledger(params) do + Map.put(params, :vevoFokonyv, CustomerLedgerFactory.get_params(params[:vevoFokonyv])) + end +end diff --git a/test/support/factories/customer_ledger_factory.ex b/test/support/factories/customer_ledger_factory.ex new file mode 100644 index 0000000..cb1b9de --- /dev/null +++ b/test/support/factories/customer_ledger_factory.ex @@ -0,0 +1,15 @@ +defmodule ExSzamlazzHu.Factories.CustomerLedgerFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + konyvelesDatum: "accounting_date", + vevoAzonosito: "customer_identifier", + vevoFokonyviSzam: "ledger_number", + folyamatosTelj: "continuous_delivery", + elszDatumTol: "settlement_date_from", + elszDatumIg: "settlement_date_to" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/header_factory.ex b/test/support/factories/header_factory.ex new file mode 100644 index 0000000..d7335a0 --- /dev/null +++ b/test/support/factories/header_factory.ex @@ -0,0 +1,35 @@ +defmodule ExSzamlazzHu.Factories.HeaderFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + keltDatum: "1997-08-29", + teljesitesDatum: "1997-08-29", + fizetesiHataridoDatum: "1997-08-29", + fizmod: "payment_method", + penznem: "HUF", + szamlaNyelve: "en", + megjegyzes: "comment", + arfolyamBank: "exchange_rate_bank", + arfolyam: "123.4", + rendelesSzam: "order_identifier", + dijbekeroSzamlaszam: "fee_request_identifier", + elolegszamla: false, + vegszamla: true, + elolegSzamlaszam: nil, + helyesbitoszamla: false, + helyesbitettSzamlaszam: nil, + dijbekero: false, + szallitolevel: false, + logoExtra: "logo_extra", + szamlaszamElotag: "PRFX", + fizetendoKorrekcio: "123.4", + fizetve: true, + arresAfa: false, + eusAfa: false, + szamlaSablon: "invoice_draft", + elonezetpdf: false + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/invoice_data_factory.ex b/test/support/factories/invoice_data_factory.ex new file mode 100644 index 0000000..5414ef3 --- /dev/null +++ b/test/support/factories/invoice_data_factory.ex @@ -0,0 +1,21 @@ +defmodule ExSzamlazzHu.Factories.InvoiceDataFactory do + @moduledoc false + + alias ExSzamlazzHu.Factories.SettingsFactory + alias ExSzamlazzHu.Factories.HeaderFactory + alias ExSzamlazzHu.Factories.CustomerFactory + alias ExSzamlazzHu.Factories.SellerFactory + alias ExSzamlazzHu.Factories.WaybillFactory + alias ExSzamlazzHu.Factories.ItemFactory + + def get_params(params \\ %{}) do + %{} + |> Map.merge(params) + |> Map.put_new_lazy(:beallitasok, fn -> SettingsFactory.get_params() end) + |> Map.put_new_lazy(:fejlec, fn -> HeaderFactory.get_params() end) + |> Map.put_new_lazy(:elado, fn -> SellerFactory.get_params() end) + |> Map.put_new_lazy(:vevo, fn -> CustomerFactory.get_params() end) + |> Map.put_new_lazy(:fuvarlevel, fn -> WaybillFactory.get_params() end) + |> Map.put_new_lazy(:tetelek, fn -> [ItemFactory.get_params()] end) + end +end diff --git a/test/support/factories/item_factory.ex b/test/support/factories/item_factory.ex new file mode 100644 index 0000000..2b7f35e --- /dev/null +++ b/test/support/factories/item_factory.ex @@ -0,0 +1,30 @@ +defmodule ExSzamlazzHu.Factories.ItemFactory do + @moduledoc false + + alias ExSzamlazzHu.Factories.ItemLedgerFactory + + def get_params(params \\ %{}) do + %{ + megnevezes: "name", + azonosito: "identifier", + mennyiseg: "1.5", + mennyisegiEgyseg: "unit", + nettoEgysegar: "net_unit_price", + afakulcs: "27", + arresAfaAlap: "margin_vat_base", + nettoErtek: "100", + afaErtek: "27", + bruttoErtek: "127", + megjegyzes: "comment", + tetelFokonyv: nil + } + |> Map.merge(params) + |> maybe_add_item_ledger() + end + + defp maybe_add_item_ledger(%{tetelFokonyv: nil} = params), do: params + + defp maybe_add_item_ledger(params) do + Map.put(params, :tetelFokonyv, ItemLedgerFactory.get_params(params[:tetelFokonyv])) + end +end diff --git a/test/support/factories/item_ledger_factory.ex b/test/support/factories/item_ledger_factory.ex new file mode 100644 index 0000000..9b103bd --- /dev/null +++ b/test/support/factories/item_ledger_factory.ex @@ -0,0 +1,15 @@ +defmodule ExSzamlazzHu.Factories.ItemLedgerFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + gazdasagiEsem: "economic_event", + gazdasagiEsemAfa: "economic_event_vat", + arbevetelFokonyviSzam: "income_ledger_number", + afaFokonyviSzam: "vat_ledger_number", + elszDatumTol: "settlement_date_from", + elszDatumIg: "settlement_date_to" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/mpl_factory.ex b/test/support/factories/mpl_factory.ex new file mode 100644 index 0000000..4ade44b --- /dev/null +++ b/test/support/factories/mpl_factory.ex @@ -0,0 +1,14 @@ +defmodule ExSzamlazzHu.Factories.MPLFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + vevokod: "buyer_identifier", + vonalkod: "barcode", + tomeg: "weight", + kulonszolgaltatasok: "extra_services", + erteknyilvanitas: "value_statement" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/ppp_factory.ex b/test/support/factories/ppp_factory.ex new file mode 100644 index 0000000..fa0e6af --- /dev/null +++ b/test/support/factories/ppp_factory.ex @@ -0,0 +1,11 @@ +defmodule ExSzamlazzHu.Factories.PPPFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + vonalkodPrefix: "barcode_prefix", + vonalkodPostfix: "barcode_postfix" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/seller_factory.ex b/test/support/factories/seller_factory.ex new file mode 100644 index 0000000..cbd27aa --- /dev/null +++ b/test/support/factories/seller_factory.ex @@ -0,0 +1,15 @@ +defmodule ExSzamlazzHu.Factories.SellerFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + bank: "bank", + bankszamlaszam: "bank_account_number", + emailReplyto: "email_reply_to", + emailTargy: "email_subject", + emailSzoveg: "email_text", + alairoNeve: "name_of_signatory" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/settings_factory.ex b/test/support/factories/settings_factory.ex new file mode 100644 index 0000000..c2a4aac --- /dev/null +++ b/test/support/factories/settings_factory.ex @@ -0,0 +1,20 @@ +defmodule ExSzamlazzHu.Factories.SettingsFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + felhasznalo: "user", + jelszo: "password", + szamlaagentkulcs: "agent_key", + eszamla: true, + szamlaLetoltes: false, + szamlaLetoltesPld: 0, + valaszVerzio: 1, + aggregator: "aggregator", + guardian: false, + cikkazoninvoice: false, + szamlaKulsoAzon: "external_invoice_identifier" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/sprinter_factory.ex b/test/support/factories/sprinter_factory.ex new file mode 100644 index 0000000..e0ea4b1 --- /dev/null +++ b/test/support/factories/sprinter_factory.ex @@ -0,0 +1,15 @@ +defmodule ExSzamlazzHu.Factories.SprinterFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + azonosito: "identifier", + feladokod: "sender_identifier", + iranykod: "code_of_direction", + csomagszam: "package_identifier", + vonalkodPostfix: "barcode_postfix", + szallitasiIdo: "delivery_deadline" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/transoflex_factory.ex b/test/support/factories/transoflex_factory.ex new file mode 100644 index 0000000..d99610c --- /dev/null +++ b/test/support/factories/transoflex_factory.ex @@ -0,0 +1,15 @@ +defmodule ExSzamlazzHu.Factories.TransoflexFactory do + @moduledoc false + + def get_params(params \\ %{}) do + %{ + azonosito: "identifier", + shipmentID: "shipment_id", + csomagszam: "package_identifier", + countryCode: "country_code", + zip: "zip", + service: "service" + } + |> Map.merge(params) + end +end diff --git a/test/support/factories/waybill_factory.ex b/test/support/factories/waybill_factory.ex new file mode 100644 index 0000000..0bde0c0 --- /dev/null +++ b/test/support/factories/waybill_factory.ex @@ -0,0 +1,41 @@ +defmodule ExSzamlazzHu.Factories.WaybillFactory do + @moduledoc false + + alias ExSzamlazzHu.Factories.MPLFactory + alias ExSzamlazzHu.Factories.PPPFactory + alias ExSzamlazzHu.Factories.TransoflexFactory + alias ExSzamlazzHu.Factories.SprinterFactory + + @three_pls [:tof, :ppp, :sprinter, :mpl] + + def get_params(params \\ %{}) do + %{ + uticel: "destination", + futarSzolgalat: "three_pl_provider", + vonalkod: "barcode", + megjegyzes: "comment", + tof: nil, + ppp: nil, + sprinter: nil, + mpl: nil + } + |> Map.merge(params) + |> maybe_add_3pl() + end + + defp maybe_add_3pl(params) do + @three_pls + |> Enum.filter(&(params[&1] != nil)) + |> Enum.reduce(params, fn three_pl, params -> + value = + case three_pl do + :mpl -> MPLFactory.get_params(params[:mpl]) + :ppp -> PPPFactory.get_params(params[:ppp]) + :tof -> TransoflexFactory.get_params(params[:tof]) + :sprinter -> SprinterFactory.get_params(params[:sprinter]) + end + + Map.put(params, three_pl, value) + end) + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs index 869559e..981aa51 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1 +1,2 @@ +ExUnit.configure(exclude: [external: true]) ExUnit.start()