All checks were successful
Build and Publish / build-release (push) Successful in 1m38s
444 lines
14 KiB
Elixir
444 lines
14 KiB
Elixir
defmodule PolicyServiceWeb.PolicyController do
|
|
use PolicyServiceWeb, :controller
|
|
use OpenApiSpex.ControllerSpecs
|
|
|
|
alias PolicyService.CommandedApp
|
|
alias PolicyService.Queries.PolicyQueries
|
|
alias PolicyService.Aggregates.PolicyId
|
|
|
|
alias PolicyService.Commands.CarPolicy
|
|
alias PolicyService.Commands.LifePolicy
|
|
alias PolicyService.Commands.FireStructurePolicy
|
|
alias PolicyService.Commands.FireContentsPolicy
|
|
|
|
alias PolicyServiceWeb.Schemas.Policy, as: S
|
|
alias PolicyServiceWeb.QueryHelpers
|
|
|
|
tags(["Policies"])
|
|
security([%{"bearerAuth" => []}])
|
|
|
|
operation(:index,
|
|
summary: "List policies",
|
|
parameters:
|
|
QueryHelpers.flop(
|
|
[:status, :policy_type, :search],
|
|
[:submitted_at, :policy_type, :status]
|
|
),
|
|
responses: [
|
|
ok: {"Policy list", "application/json", S.PolicyListResponse},
|
|
bad_request: {"Invalid params", "application/json", S.ErrorResponse}
|
|
]
|
|
)
|
|
|
|
def index(conn, params) do
|
|
org_id = conn.assigns[:org_id] || "test"
|
|
|
|
case PolicyQueries.list_by_org(org_id, params) do
|
|
{:ok, {policies, meta}} ->
|
|
conn
|
|
|> put_status(:ok)
|
|
|> json(%{
|
|
data: Enum.map(policies, &policy_summary/1),
|
|
meta: meta_json(meta)
|
|
})
|
|
|
|
{:error, _} ->
|
|
conn |> put_status(:bad_request) |> json(%{error: "invalid parameters"})
|
|
end
|
|
end
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GET /api/policies/:application_id
|
|
# ---------------------------------------------------------------------------
|
|
|
|
operation(:show,
|
|
summary: "Get policy detail",
|
|
parameters: [
|
|
application_id: [in: :path, type: :string, required: true]
|
|
],
|
|
responses: [
|
|
ok: {"Policy detail", "application/json", S.PolicyDetailResponse},
|
|
not_found: {"Not found", "application/json", S.ErrorResponse}
|
|
]
|
|
)
|
|
|
|
def show(conn, %{"application_id" => application_id}) do
|
|
org_id = conn.assigns[:org_id] || "test"
|
|
|
|
case PolicyQueries.get_by_application_id(org_id, application_id) do
|
|
{:ok, policy} ->
|
|
conn |> put_status(:ok) |> json(%{data: policy_detail(policy)})
|
|
|
|
{:error, :not_found} ->
|
|
conn |> put_status(:not_found) |> json(%{error: "policy not found"})
|
|
end
|
|
end
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# POST /api/policies
|
|
# ---------------------------------------------------------------------------
|
|
|
|
operation(:create,
|
|
summary: "Submit a policy quote request",
|
|
request_body: {"Quote request", "application/json", S.CreatePolicyRequest, required: true},
|
|
responses: [
|
|
created: {"Submitted", "application/json", S.QuoteResponse},
|
|
unprocessable_entity: {"Validation error", "application/json", S.ErrorResponse}
|
|
]
|
|
)
|
|
|
|
def create(conn, params) do
|
|
application_id = Ecto.UUID.generate()
|
|
org_id = conn.assigns[:org_id] || "test"
|
|
submitted_by = conn.assigns[:user_id] || "test"
|
|
|
|
with {:ok, policy_type} <- parse_policy_type(params["policy_type"]),
|
|
{:ok, insured} <- parse_insured(params["insured"]),
|
|
{:ok, buyer} <- parse_buyer(params["buyer"]),
|
|
{:ok, policy_details} <- parse_policy_details(policy_type, params["policy_details"]),
|
|
{:ok, providers} <- parse_providers(params["selected_providers"]) do
|
|
command =
|
|
case policy_type do
|
|
"car" ->
|
|
%CarPolicy.SubmitPolicyApplication{
|
|
id: PolicyId.new(org_id, policy_type, application_id),
|
|
submitted_by: submitted_by,
|
|
insured: insured,
|
|
buyer: buyer,
|
|
policy_details: policy_details,
|
|
selected_providers: providers
|
|
}
|
|
|
|
"life" ->
|
|
%LifePolicy.SubmitPolicyApplication{
|
|
id: PolicyId.new(org_id, policy_type, application_id),
|
|
submitted_by: submitted_by,
|
|
insured: insured,
|
|
buyer: buyer,
|
|
policy_details: policy_details,
|
|
selected_providers: providers
|
|
}
|
|
|
|
"fire_structure" ->
|
|
%FireStructurePolicy.SubmitPolicyApplication{
|
|
id: PolicyId.new(org_id, policy_type, application_id),
|
|
submitted_by: submitted_by,
|
|
insured: insured,
|
|
buyer: buyer,
|
|
policy_details: policy_details,
|
|
selected_providers: providers
|
|
}
|
|
|
|
"fire_contents" ->
|
|
%FireContentsPolicy.SubmitPolicyApplication{
|
|
id: PolicyId.new(org_id, policy_type, application_id),
|
|
submitted_by: submitted_by,
|
|
insured: insured,
|
|
buyer: buyer,
|
|
policy_details: policy_details,
|
|
selected_providers: providers
|
|
}
|
|
end
|
|
|
|
case CommandedApp.dispatch(command, consistency: :strong) do
|
|
:ok ->
|
|
conn
|
|
|> put_status(:created)
|
|
|> json(%{application_id: application_id, status: "awaiting_quotes"})
|
|
|
|
{:error, reason} ->
|
|
conn |> put_status(:unprocessable_entity) |> json(%{error: inspect(reason)})
|
|
end
|
|
else
|
|
{:error, reason} ->
|
|
conn |> put_status(:unprocessable_entity) |> json(%{error: inspect(reason)})
|
|
end
|
|
end
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# POST /api/policies/:application_id/accept
|
|
# ---------------------------------------------------------------------------
|
|
|
|
operation(:accept,
|
|
summary: "Accept a quote plan and trigger solicitation",
|
|
parameters: [
|
|
application_id: [in: :path, type: :string, required: true]
|
|
],
|
|
request_body: {"Accept quote", "application/json", S.AcceptQuoteRequest, required: true},
|
|
responses: [
|
|
ok: {"Accepted", "application/json", S.PolicyDetailResponse},
|
|
not_found: {"Not found", "application/json", S.ErrorResponse},
|
|
unprocessable_entity: {"Error", "application/json", S.ErrorResponse}
|
|
]
|
|
)
|
|
|
|
def accept(conn, %{"application_id" => application_id} = params) do
|
|
org_id = conn.assigns[:org_id] || "test"
|
|
|
|
with {:ok, policy} <- PolicyQueries.get_by_application_id(org_id, application_id) do
|
|
command =
|
|
case policy.policy_type do
|
|
"car" ->
|
|
%CarPolicy.AcceptQuoteAndSolicit{
|
|
id: PolicyId.new(org_id, policy.policy_type, application_id),
|
|
accepted_by: params["accepted_by"] || "system",
|
|
accepted_plan_id: params["accepted_plan_id"]
|
|
}
|
|
|
|
"life" ->
|
|
%LifePolicy.AcceptQuoteAndSolicit{
|
|
id: PolicyId.new(org_id, policy.policy_type, application_id),
|
|
accepted_by: params["accepted_by"] || "system",
|
|
accepted_plan_id: params["accepted_plan_id"]
|
|
}
|
|
|
|
"fire_structure" ->
|
|
%FireStructurePolicy.AcceptQuoteAndSolicit{
|
|
id: PolicyId.new(org_id, policy.policy_type, application_id),
|
|
accepted_by: params["accepted_by"] || "system",
|
|
accepted_plan_id: params["accepted_plan_id"]
|
|
}
|
|
|
|
"fire_contents" ->
|
|
%FireContentsPolicy.AcceptQuoteAndSolicit{
|
|
id: PolicyId.new(org_id, policy.policy_type, application_id),
|
|
accepted_by: params["accepted_by"] || "system",
|
|
accepted_plan_id: params["accepted_plan_id"]
|
|
}
|
|
end
|
|
|
|
case CommandedApp.dispatch(command, consistency: :strong) do
|
|
:ok ->
|
|
{:ok, updated} = PolicyQueries.get_by_application_id(org_id, application_id)
|
|
conn |> put_status(:ok) |> json(%{data: policy_detail(updated)})
|
|
|
|
{:error, :plan_not_found} ->
|
|
conn |> put_status(:not_found) |> json(%{error: "plan not found"})
|
|
|
|
{:error, reason} ->
|
|
conn |> put_status(:unprocessable_entity) |> json(%{error: inspect(reason)})
|
|
end
|
|
else
|
|
{:error, :not_found} ->
|
|
conn |> put_status(:not_found) |> json(%{error: "policy not found"})
|
|
end
|
|
end
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Serializers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
defp policy_summary(p) do
|
|
%{
|
|
application_id: p.application_id,
|
|
policy_type: p.policy_type,
|
|
status: p.status,
|
|
insured: p.insured,
|
|
buyer: p.buyer,
|
|
policy_details: p.policy_details,
|
|
provider_policy_number: p.provider_policy_number,
|
|
submitted_at: p.submitted_at
|
|
}
|
|
end
|
|
|
|
defp policy_detail(p) do
|
|
%{
|
|
application_id: p.application_id,
|
|
org_id: p.org_id,
|
|
submitted_by: p.submitted_by,
|
|
policy_type: p.policy_type,
|
|
status: p.status,
|
|
insured: p.insured,
|
|
buyer: p.buyer,
|
|
policy_details: p.policy_details,
|
|
selected_providers: p.selected_providers,
|
|
quotes: p.quotes,
|
|
accepted_plan_id: p.accepted_plan_id,
|
|
accepted_by: p.accepted_by,
|
|
provider_policy_number: p.provider_policy_number,
|
|
premium: p.premium,
|
|
effective_date: p.effective_date,
|
|
expiry_date: p.expiry_date,
|
|
submitted_at: p.submitted_at,
|
|
solicitation_sent_at: p.solicitation_sent_at,
|
|
issued_at: p.issued_at
|
|
}
|
|
end
|
|
|
|
defp meta_json(meta) do
|
|
%{
|
|
total_count: meta.total_count,
|
|
total_pages: meta.total_pages,
|
|
current_page: meta.current_page,
|
|
page_size: meta.page_size,
|
|
has_next: meta.has_next_page?,
|
|
has_prev: meta.has_previous_page?
|
|
}
|
|
end
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Parse helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
defp parse_policy_type(type) when type in ["car", "life", "fire_structure", "fire_contents"],
|
|
do: {:ok, type}
|
|
|
|
defp parse_policy_type(_), do: {:error, :invalid_policy_type}
|
|
|
|
# insured — individual
|
|
defp parse_insured(info) do
|
|
case info["type"] do
|
|
"individual" ->
|
|
case info["date_of_birth"] do
|
|
nil ->
|
|
{:error, :missing_date_of_birth}
|
|
|
|
dob ->
|
|
{:ok,
|
|
%{
|
|
"type" => "individual",
|
|
"name" => info["name"],
|
|
"date_of_birth" => dob,
|
|
"document_id" => info["document_id"],
|
|
"email" => info["email"],
|
|
"phone" => info["phone"],
|
|
"address" => info["address"]
|
|
}}
|
|
end
|
|
|
|
"corporate" ->
|
|
{:ok,
|
|
%{
|
|
"type" => "corporate",
|
|
"company_name" => info["company_name"],
|
|
"ruc" => info["ruc"],
|
|
"legal_rep_name" => info["legal_rep_name"],
|
|
"legal_rep_document" => info["legal_rep_document"],
|
|
"email" => info["email"],
|
|
"phone" => info["phone"],
|
|
"address" => info["address"]
|
|
}}
|
|
|
|
_ ->
|
|
{:error, :invalid_insured_type}
|
|
end
|
|
end
|
|
|
|
# buyer — individual
|
|
defp parse_buyer(info) do
|
|
case info["type"] do
|
|
"individual" ->
|
|
case info["date_of_birth"] do
|
|
nil ->
|
|
{:error, :missing_date_of_birth}
|
|
|
|
dob ->
|
|
{:ok,
|
|
%{
|
|
"type" => "individual",
|
|
"name" => info["name"],
|
|
"date_of_birth" => dob,
|
|
"document_id" => info["document_id"],
|
|
"email" => info["email"],
|
|
"phone" => info["phone"],
|
|
"address" => info["address"]
|
|
}}
|
|
end
|
|
|
|
"corporate" ->
|
|
{:ok,
|
|
%{
|
|
"type" => "corporate",
|
|
"company_name" => info["company_name"],
|
|
"ruc" => info["ruc"],
|
|
"legal_rep_name" => info["legal_rep_name"],
|
|
"legal_rep_document" => info["legal_rep_document"],
|
|
"email" => info["email"],
|
|
"phone" => info["phone"],
|
|
"address" => info["address"]
|
|
}}
|
|
|
|
_ ->
|
|
{:error, :invalid_buyer_type}
|
|
end
|
|
end
|
|
|
|
# individual — has document_id
|
|
|
|
# car details
|
|
defp parse_policy_details("car", nil), do: {:error, :missing_policy_details}
|
|
|
|
defp parse_policy_details("car", d) do
|
|
{:ok,
|
|
%{
|
|
"plate" => d["plate"],
|
|
"make" => d["make"],
|
|
"model" => d["model"],
|
|
"year" => d["year"],
|
|
"use_type" => d["use_type"],
|
|
"car_type" => d["car_type"],
|
|
"chassis_number" => d["chassis_number"],
|
|
"engine_number" => d["engine_number"],
|
|
"rc_limits" => d["rc_limits"],
|
|
"market_value" => d["market_value"],
|
|
"requested_value" => d["requested_value"]
|
|
}}
|
|
end
|
|
|
|
# life details
|
|
defp parse_policy_details("life", nil), do: {:error, :missing_policy_details}
|
|
|
|
defp parse_policy_details("life", d) do
|
|
{:ok,
|
|
%{
|
|
"coverage_type" => d["coverage_type"],
|
|
"coverage_amount" => d["coverage_amount"],
|
|
"coverage_years" => d["coverage_years"],
|
|
"smoker" => d["smoker"],
|
|
"medications" => d["medications"],
|
|
"surgeries" => d["surgeries"],
|
|
"weight" => d["weight"],
|
|
"height" => d["height"]
|
|
}}
|
|
end
|
|
|
|
# fire_structure details
|
|
defp parse_policy_details("fire_structure", nil), do: {:error, :missing_policy_details}
|
|
|
|
defp parse_policy_details("fire_structure", d) do
|
|
{:ok,
|
|
%{
|
|
"location" => d["location"],
|
|
"property_value" => d["property_value"],
|
|
"property_use" => d["property_use"],
|
|
"security_measures" => d["security_measures"],
|
|
"market_value" => d["market_value"]
|
|
}}
|
|
end
|
|
|
|
# fire_contents details
|
|
defp parse_policy_details("fire_contents", nil), do: {:error, :missing_policy_details}
|
|
|
|
defp parse_policy_details("fire_contents", d) do
|
|
{:ok,
|
|
%{
|
|
"location" => d["location"],
|
|
"contents_value" => d["contents_value"],
|
|
"property_use" => d["property_use"],
|
|
"security_measures" => d["security_measures"],
|
|
"high_value_items" => d["high_value_items"]
|
|
}}
|
|
end
|
|
|
|
defp parse_policy_details(_, _), do: {:error, :invalid_policy_details}
|
|
|
|
defp parse_providers(nil), do: {:error, :missing_providers}
|
|
defp parse_providers([]), do: {:error, :no_providers_selected}
|
|
|
|
defp parse_providers(list) when is_list(list) do
|
|
{:ok, Enum.map(list, fn p -> %{provider_id: p["provider_id"], email: p["email"]} end)}
|
|
end
|
|
|
|
defp parse_providers(_), do: {:error, :invalid_providers}
|
|
end
|