This commit is contained in:
@@ -1,85 +0,0 @@
|
||||
# lib/policy_service_web/controllers/car_policy_controller.ex
|
||||
|
||||
defmodule PolicyServiceWeb.CarPolicyController do
|
||||
use PolicyServiceWeb, :controller
|
||||
use OpenApiSpex.ControllerSpecs
|
||||
|
||||
alias OpenApiSpex.Schema
|
||||
alias PolicyServiceWeb.Schemas.CarPolicy.{QuoteRequest, QuoteResponse}
|
||||
alias PolicyService.Commands.Car.SubmitCarPolicyApplication
|
||||
|
||||
tags(["Car Policy"])
|
||||
security([%{"bearerAuth" => []}])
|
||||
|
||||
operation(:request_quote,
|
||||
summary: "Solicitar cotización de seguro de auto",
|
||||
description: "Envía una solicitud de cotización a los proveedores seleccionados",
|
||||
request_body: {"Quote request body", "application/json", QuoteRequest, required: true},
|
||||
responses: [
|
||||
created: {"Solicitud creada exitosamente", "application/json", QuoteResponse},
|
||||
unprocessable_entity:
|
||||
{"Error de validación", "application/json",
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
errors: %Schema{type: :object}
|
||||
}
|
||||
}}
|
||||
]
|
||||
)
|
||||
|
||||
def request_quote(conn, params) do
|
||||
user = %{"id" => "test", "org_id" => "test"}
|
||||
|
||||
cmd = %SubmitCarPolicyApplication{
|
||||
application_id: Ecto.UUID.generate(),
|
||||
org_id: user["org_id"],
|
||||
submitted_by: user["id"],
|
||||
applicant_info: %{
|
||||
name: params["applicant_info"]["name"],
|
||||
date_of_birth: Date.from_iso8601!(params["applicant_info"]["date_of_birth"]),
|
||||
document_id: params["applicant_info"]["document_id"]
|
||||
},
|
||||
car_details: %{
|
||||
plate: params["car_details"]["plate"],
|
||||
make: params["car_details"]["make"],
|
||||
model: params["car_details"]["model"],
|
||||
year: params["car_details"]["year"],
|
||||
car_value: parse_number(params["car_details"]["car_value"]),
|
||||
use_type: String.to_atom(params["car_details"]["use_type"]),
|
||||
car_type: String.to_atom(params["car_details"]["car_type"]),
|
||||
chassis_number: params["car_details"]["chassis_number"],
|
||||
engine_number: params["car_details"]["engine_number"]
|
||||
},
|
||||
selected_providers:
|
||||
Enum.map(params["selected_providers"], fn p ->
|
||||
%{id: p["id"], email: p["email"]}
|
||||
end)
|
||||
}
|
||||
|
||||
case PolicyService.CommandedApp.dispatch(cmd) do
|
||||
:ok ->
|
||||
conn
|
||||
|> put_status(:created)
|
||||
|> json(%{
|
||||
application_id: cmd.applicant_info,
|
||||
status: "awaiting_quotes"
|
||||
})
|
||||
|
||||
{:error, reason} ->
|
||||
conn
|
||||
|> put_status(:unprocessable_entity)
|
||||
|> json(%{errors: reason})
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_number(val) when is_float(val), do: val
|
||||
defp parse_number(val) when is_integer(val), do: val * 1.0
|
||||
|
||||
defp parse_number(val) when is_binary(val) do
|
||||
case Float.parse(val) do
|
||||
{f, _} -> f
|
||||
:error -> raise "invalid number: #{val}"
|
||||
end
|
||||
end
|
||||
end
|
||||
378
lib/policy_service_web/controllers/policy_controller.ex
Normal file
378
lib/policy_service_web/controllers/policy_controller.ex
Normal file
@@ -0,0 +1,378 @@
|
||||
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
|
||||
|
||||
tags(["Policies"])
|
||||
security([%{"bearerAuth" => []}])
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/policies
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
operation(:index,
|
||||
summary: "List policies",
|
||||
parameters: [
|
||||
"page[number]": [in: :query, type: :integer, required: false],
|
||||
"page[size]": [in: :query, type: :integer, required: false],
|
||||
"filters[0][field]": [in: :query, type: :string, required: false],
|
||||
"filters[0][op]": [in: :query, type: :string, required: false],
|
||||
"filters[0][value]": [in: :query, type: :string, required: false],
|
||||
"filters[1][field]": [in: :query, type: :string, required: false],
|
||||
"filters[1][op]": [in: :query, type: :string, required: false],
|
||||
"filters[1][value]": [in: :query, type: :string, required: false],
|
||||
"order_by[]": [in: :query, type: :string, required: false]
|
||||
],
|
||||
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),
|
||||
quote_id: params["quote_id"],
|
||||
plan_id: params["plan_id"],
|
||||
solicitation_fields: params["solicitation_fields"] || %{}
|
||||
}
|
||||
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, :quote_not_found} ->
|
||||
conn |> put_status(:not_found) |> json(%{error: "quote not found"})
|
||||
|
||||
{: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
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /api/policies/:application_id/solicitation-url
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
operation(:solicitation_url,
|
||||
summary: "Get fresh presigned download URL for solicitation PDF",
|
||||
parameters: [
|
||||
application_id: [in: :path, type: :string, required: true],
|
||||
version: [in: :query, type: :integer, required: false]
|
||||
],
|
||||
responses: [
|
||||
ok: {"Presigned URL", "application/json", S.SolicitationUrlResponse},
|
||||
not_found: {"Not found", "application/json", S.ErrorResponse}
|
||||
]
|
||||
)
|
||||
|
||||
def solicitation_url(conn, %{"application_id" => application_id} = params) do
|
||||
org_id = conn.assigns[:org_id] || "test"
|
||||
version = String.to_integer(params["version"] || "1")
|
||||
|
||||
case PolicyQueries.get_by_application_id(org_id, application_id) do
|
||||
{:error, :not_found} ->
|
||||
conn |> put_status(:not_found) |> json(%{error: "policy not found"})
|
||||
|
||||
{:ok, %{solicitation_id: nil}} ->
|
||||
conn |> put_status(:not_found) |> json(%{error: "no solicitation yet"})
|
||||
|
||||
{:ok, policy} ->
|
||||
url =
|
||||
"#{solicitation_service_url()}/api/solicitations/#{policy.solicitation_id}/download-url"
|
||||
|
||||
case Req.get(url,
|
||||
params: [org_id: org_id, application_id: application_id, version: version]
|
||||
) do
|
||||
{:ok, %{status: 200, body: body}} ->
|
||||
conn |> put_status(:ok) |> json(body)
|
||||
|
||||
{:ok, %{status: status, body: body}} ->
|
||||
conn
|
||||
|> put_status(:bad_gateway)
|
||||
|> json(%{error: "solicitation service returned #{status}: #{inspect(body)}"})
|
||||
|
||||
{:error, reason} ->
|
||||
conn |> put_status(:bad_gateway) |> json(%{error: inspect(reason)})
|
||||
end
|
||||
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,
|
||||
policy_number: p.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_quote_id: p.accepted_quote_id,
|
||||
accepted_plan_id: p.accepted_plan_id,
|
||||
accepted_provider_id: p.accepted_provider_id,
|
||||
accepted_at: p.accepted_at,
|
||||
solicitation_id: p.solicitation_id,
|
||||
solicitation_s3_key: p.solicitation_s3_key,
|
||||
policy_number: p.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
|
||||
}}
|
||||
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"]
|
||||
}}
|
||||
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}
|
||||
|
||||
defp solicitation_service_url do
|
||||
Application.get_env(:policy_service, :solicitation_service_url, "http://localhost:8081")
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user