defmodule ProviderService.Aggregates.Provider do defstruct [ :provider_id, :name, :email, :phone, :contact_name, :ruc, :address, :active, templates: %{}, default_templates: %{} ] alias ProviderService.Commands.{ RegisterProvider, UpdateProvider, DeactivateProvider, ReactivateProvider, AddProviderTemplate, ActivateProviderTemplate, DeactivateProviderTemplate, SetDefaultProviderTemplate, RemoveProviderTemplate } alias ProviderService.Events.{ ProviderRegistered, ProviderUpdated, ProviderDeactivated, ProviderReactivated, ProviderTemplateAdded, ProviderTemplateActivated, ProviderTemplateDeactivated, ProviderTemplateDefaultSet, ProviderTemplateRemoved } # --------------------------------------------------------------------------- # Execute — Provider # --------------------------------------------------------------------------- def execute(%__MODULE__{provider_id: nil}, %RegisterProvider{} = cmd) do %ProviderRegistered{ provider_id: cmd.provider_id, name: cmd.name, email: cmd.email, phone: cmd.phone, contact_name: cmd.contact_name, ruc: cmd.ruc, address: cmd.address, registered_at: DateTime.utc_now() } end def execute(%__MODULE__{active: false}, %UpdateProvider{}), do: {:error, :provider_inactive} def execute(%__MODULE__{} = agg, %UpdateProvider{} = cmd) do %ProviderUpdated{ provider_id: agg.provider_id, name: cmd.name, email: cmd.email, phone: cmd.phone, contact_name: cmd.contact_name, ruc: cmd.ruc, address: cmd.address, updated_at: DateTime.utc_now() } end def execute(%__MODULE__{active: false}, %DeactivateProvider{}), do: {:error, :already_inactive} def execute(%__MODULE__{} = agg, %DeactivateProvider{} = cmd) do %ProviderDeactivated{ provider_id: agg.provider_id, deactivated_by: cmd.deactivated_by, deactivated_at: DateTime.utc_now() } end def execute(%__MODULE__{active: true}, %ReactivateProvider{}), do: {:error, :already_active} def execute(%__MODULE__{} = agg, %ReactivateProvider{} = cmd) do %ProviderReactivated{ provider_id: agg.provider_id, reactivated_by: cmd.reactivated_by, reactivated_at: DateTime.utc_now() } end # --------------------------------------------------------------------------- # Execute — Templates # --------------------------------------------------------------------------- def execute(%__MODULE__{active: false}, %AddProviderTemplate{}), do: {:error, :provider_inactive} def execute(%__MODULE__{} = agg, %AddProviderTemplate{} = cmd) do existing = get_in(agg.templates, [cmd.policy_type, cmd.client_type]) || [] version = length(existing) + 1 %ProviderTemplateAdded{ provider_id: agg.provider_id, template_id: cmd.template_id, policy_type: cmd.policy_type, client_type: cmd.client_type, s3_key: cmd.s3_key, fields: cmd.fields, version: version, added_at: DateTime.utc_now() } end def execute(%__MODULE__{} = agg, %ActivateProviderTemplate{} = cmd) do case find_template(agg, cmd.policy_type, cmd.client_type, cmd.template_id) do nil -> {:error, :template_not_found} _ -> %ProviderTemplateActivated{ provider_id: agg.provider_id, template_id: cmd.template_id, policy_type: cmd.policy_type, client_type: cmd.client_type, activated_at: DateTime.utc_now() } end end def execute(%__MODULE__{} = agg, %DeactivateProviderTemplate{} = cmd) do case find_template(agg, cmd.policy_type, cmd.client_type, cmd.template_id) do nil -> {:error, :template_not_found} _ -> %ProviderTemplateDeactivated{ provider_id: agg.provider_id, template_id: cmd.template_id, policy_type: cmd.policy_type, client_type: cmd.client_type, deactivated_at: DateTime.utc_now() } end end def execute(%__MODULE__{} = agg, %SetDefaultProviderTemplate{} = cmd) do case find_template(agg, cmd.policy_type, cmd.client_type, cmd.template_id) do nil -> {:error, :template_not_found} %{active: false} -> {:error, :template_not_active} _ -> %ProviderTemplateDefaultSet{ provider_id: agg.provider_id, template_id: cmd.template_id, policy_type: cmd.policy_type, client_type: cmd.client_type, set_at: DateTime.utc_now() } end end def execute(%__MODULE__{} = agg, %RemoveProviderTemplate{} = cmd) do case find_template(agg, cmd.policy_type, cmd.client_type, cmd.template_id) do nil -> {:error, :template_not_found} _ -> %ProviderTemplateRemoved{ provider_id: agg.provider_id, template_id: cmd.template_id, policy_type: cmd.policy_type, client_type: cmd.client_type, removed_at: DateTime.utc_now() } end end # --------------------------------------------------------------------------- # Apply — Provider # --------------------------------------------------------------------------- def apply(%__MODULE__{} = agg, %ProviderRegistered{} = e) do %__MODULE__{ agg | provider_id: e.provider_id, name: e.name, email: e.email, phone: e.phone, contact_name: e.contact_name, ruc: e.ruc, address: e.address, active: true } end def apply(%__MODULE__{} = agg, %ProviderUpdated{} = e) do %__MODULE__{ agg | name: e.name, email: e.email, phone: e.phone, contact_name: e.contact_name, ruc: e.ruc, address: e.address } end def apply(%__MODULE__{} = agg, %ProviderDeactivated{}), do: %__MODULE__{agg | active: false} def apply(%__MODULE__{} = agg, %ProviderReactivated{}), do: %__MODULE__{agg | active: true} # --------------------------------------------------------------------------- # Apply — Templates # --------------------------------------------------------------------------- def apply(%__MODULE__{} = agg, %ProviderTemplateAdded{} = e) do existing = get_in(agg.templates, [e.policy_type, e.client_type]) || [] templates = agg.templates |> Map.update(e.policy_type, %{e.client_type => []}, fn inner -> Map.update(inner, e.client_type, [], fn list -> list end) end) |> put_in( [e.policy_type, e.client_type], existing ++ [ %{ template_id: e.template_id, s3_key: e.s3_key, fields: e.fields, version: e.version, active: true } ] ) %__MODULE__{agg | templates: templates} end def apply(%__MODULE__{} = agg, %ProviderTemplateActivated{} = e) do templates = update_template( agg.templates, e.policy_type, e.client_type, e.template_id, &Map.put(&1, :active, true) ) %__MODULE__{agg | templates: templates} end def apply(%__MODULE__{} = agg, %ProviderTemplateDeactivated{} = e) do templates = update_template( agg.templates, e.policy_type, e.client_type, e.template_id, &Map.put(&1, :active, false) ) template_id = e.template_id # Clear default if the deactivated template was the default default_templates = case get_in(agg.default_templates, [e.policy_type, e.client_type]) do ^template_id -> update_in(agg.default_templates, [e.policy_type], &Map.delete(&1, e.client_type)) _ -> agg.default_templates end %__MODULE__{agg | templates: templates, default_templates: default_templates} end def apply(%__MODULE__{} = agg, %ProviderTemplateDefaultSet{} = e) do default_templates = agg.default_templates |> Map.update(e.policy_type, %{e.client_type => e.template_id}, fn inner -> Map.put(inner, e.client_type, e.template_id) end) %__MODULE__{agg | default_templates: default_templates} end def apply(%__MODULE__{} = agg, %ProviderTemplateRemoved{} = e) do templates = agg.templates |> update_in([e.policy_type, e.client_type], fn list -> Enum.reject(list || [], &(&1.template_id == e.template_id)) end) template_id = e.template_id # Clear default if the removed template was the default default_templates = case get_in(agg.default_templates, [e.policy_type, e.client_type]) do ^template_id -> update_in(agg.default_templates, [e.policy_type], &Map.delete(&1, e.client_type)) _ -> agg.default_templates end %__MODULE__{agg | templates: templates, default_templates: default_templates} end # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- # templates structure: %{ policy_type => %{ client_type => [%{template_id, ...}] } } # default_templates: %{ policy_type => %{ client_type => template_id } } defp find_template(agg, policy_type, client_type, template_id) do agg.templates |> get_in([policy_type, client_type]) |> List.wrap() |> Enum.find(&(&1.template_id == template_id)) end defp update_template(templates, policy_type, client_type, template_id, fun) do templates |> Map.update(policy_type, %{}, fn inner -> Map.update(inner, client_type, [], fn list -> Enum.map(list, fn t -> if t.template_id == template_id, do: fun.(t), else: t end) end) end) end end