defmodule PolicyService.Projectors.PolicyProjector do use Commanded.Projections.Ecto, application: PolicyService.CommandedApp, repo: PolicyService.Repo, name: "PolicyApplicationProjection", consistency: :strong alias PolicyService.Events.Policy.{ PolicyApplicationSubmitted, ProviderQuoteReceived, AllQuotesReceived, QuoteAccepted, SolicitationRequestSent, PolicyIssued } alias PolicyService.Projections.PolicyApplication import Ecto.Query project(%PolicyApplicationSubmitted{} = e, _meta, fn multi -> Ecto.Multi.insert(multi, :policy_application, %PolicyApplication{ id: to_string(e.id), application_id: e.id.application_id, org_id: e.id.org_id, submitted_by: e.submitted_by, policy_type: e.id.policy_type, insured: atomize(e.insured), buyer: atomize(e.buyer), insured_object: atomize(e.insured_object), selected_providers: Enum.map(e.selected_providers, & &1["provider_id"]), quotes: %{}, status: "quote_requested", submitted_at: parse_datetime(e.submitted_at) }) end) project(%ProviderQuoteReceived{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(PolicyApplication, to_string(e.id))} end) |> Ecto.Multi.update(:policy_application, fn %{fetch: p} -> quote_data = %{ "quote_id" => e.quote_id, "provider_id" => e.provider_id, "valid_until" => e.valid_until, "received_at" => parse_datetime(e.received_at), "plans" => e.plans || [] } Ecto.Changeset.change(p, quotes: Map.put(p.quotes, e.provider_id, quote_data)) end) end) project(%AllQuotesReceived{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(PolicyApplication, to_string(e.id))} end) |> Ecto.Multi.update(:policy_application, fn %{fetch: p} -> Ecto.Changeset.change(p, status: "quotes_received") end) end) project(%QuoteAccepted{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(PolicyApplication, to_string(e.id))} end) |> Ecto.Multi.update(:policy_application, fn %{fetch: p} -> Ecto.Changeset.change(p, accepted_plan_id: e.plan.plan_id, accepted_by: e.accepted_by ) end) end) project(%SolicitationRequestSent{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(PolicyApplication, to_string(e.id))} end) |> Ecto.Multi.update(:policy_application, fn %{fetch: p} -> Ecto.Changeset.change(p, status: "awaiting_policy" ) end) end) project(%PolicyIssued{} = e, _meta, fn multi -> multi |> Ecto.Multi.run(:fetch, fn repo, _ -> {:ok, repo.get!(PolicyApplication, to_string(e.id))} end) |> Ecto.Multi.update(:policy_application, fn %{fetch: p} -> Ecto.Changeset.change(p, provider_policy_number: e.provider_policy_number, effective_date: parse_date(e.effective_date), expiry_date: parse_date(e.expiry_date), issued_at: parse_datetime(e.issued_at), status: "issued" ) end) end) # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- defp atomize(nil), do: nil defp atomize(map) when is_map(map) do Map.new(map, fn {k, v} -> {if(is_atom(k), do: Atom.to_string(k), else: k), v} end) end defp parse_datetime(nil), do: nil defp parse_datetime(%DateTime{} = dt), do: dt defp parse_datetime(str) when is_binary(str) do case DateTime.from_iso8601(str) do {:ok, dt, _} -> dt _ -> nil end end defp parse_date(nil), do: nil defp parse_date(%Date{} = d), do: d defp parse_date(str) when is_binary(str) do case Date.from_iso8601(str) do {:ok, d} -> d _ -> nil end end end