Files
policy-service/lib/policy_service_web/controllers/policy_controller.ex
HaimKortovich 2adb948b8e
All checks were successful
Build and Publish / build-release (push) Successful in 1m24s
fix applicant and filters
2026-04-22 16:24:20 -05:00

311 lines
9.8 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 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, applicant_info} <- parse_applicant_info(params["applicant_info"]),
{: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,
applicant_info: applicant_info,
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"]
}
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,
applicant_info: p.applicant_info,
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,
applicant_info: p.applicant_info,
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"], do: {:ok, type}
defp parse_policy_type(_), do: {:error, :invalid_policy_type}
# individual — has document_id
defp parse_applicant_info(%{"document_id" => doc} = info)
when is_binary(doc) and byte_size(doc) > 0 do
case info["date_of_birth"] do
nil ->
{:error, :missing_date_of_birth}
dob ->
{:ok,
%{
"name" => info["name"],
"date_of_birth" => dob,
"document_id" => doc,
"client_type" => "natural"
}}
end
end
# corporate — has ruc
defp parse_applicant_info(%{"ruc" => ruc} = info)
when is_binary(ruc) and byte_size(ruc) > 0 do
{:ok,
%{
"company_name" => info["company_name"],
"ruc" => ruc,
"legal_rep_name" => info["legal_rep_name"],
"legal_rep_document" => info["legal_rep_document"],
"client_type" => "juridico"
}}
end
defp parse_applicant_info(_), do: {:error, :invalid_applicant_info}
# 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"],
"car_value" => d["car_value"],
"use_type" => d["use_type"],
"car_type" => d["car_type"],
"chassis_number" => d["chassis_number"],
"engine_number" => d["engine_number"]
}}
end
# life details
defp parse_policy_details("life", nil), do: {:error, :missing_policy_details}
defp parse_policy_details("life", d) do
{:ok,
%{
"coverage_amount" => d["coverage_amount"],
"beneficiary" => d["beneficiary"]
}}
end
# fire details
defp parse_policy_details("fire", nil), do: {:error, :missing_policy_details}
defp parse_policy_details("fire", d) do
{:ok,
%{
"property_address" => d["property_address"],
"property_value" => d["property_value"]
}}
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