Proposal Selection
How to follow this chapter
Inputs: The chosen business line and investment category, then responses entered by the client.
Steps and expected result: Resolve lookup IDs, collect response IDs, calculate risk, and let the client choose a proposal. Carry these values into Plan creation.
Alternative or later actions: The recommended proposal is the main path. Choosing another proposal can require deviation reasons.
DTO source: BusinessLineOutputModel, InvestmentCategoryOutputModel, ProposalOutputModel, QuestionnaireOutputModel, ReasonToChangeProposalOutputModel, ReasonToChangeProposalResponseOutputModel, RiskCategorizationOutputModel
Variables assigned in an earlier block are reused by later blocks. Client-entered values come from your UI; keep identifiers and lookup results in the current client context. Do not create a fresh context between steps.
Vorsorge (pension):
- 3a pillar (3a)
- Vested benefits (FZ)
Finance (wealth management):
- Invest (3b)
Note
Querying business lines
Action: Resolve their reference entries.
Keep / expected result: Keep businessLineId and investmentCategoryId.
var businessLines = await httpClient.GetFromJsonAsync<IList<BusinessLineOutputModel>>("api/v1/business-lines");
var businessLineVorsorge = businessLines.First(x => x.Code == "VORSORGE");
var investmentCategories = await httpClient.GetFromJsonAsync<IList<InvestmentCategoryOutputModel>>("api/v1/investment-categories/business-line-id/" + businessLineVorsorge.Id);
var investmentCategory3A = investmentCategories.First(x => x.Code == "3A");
HttpRequest businessLineRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("business-lines/code/VORSORGE"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
.header("Content-Type", "application/json")
.build();
var response = httpClient.send(businessLineRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
BusinessLineOutputModel businessLineOutputModelObj = new Gson().fromJson(response.body(), BusinessLineOutputModel.class);
var businessLineId = businessLineOutputModelObj.Id;
HttpRequest investmentCategoryRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("investment-categories/code/3A"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
.header("Content-Type", "application/json")
.build();
response = httpClient.send(investmentCategoryRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
InvestmentCategoryOutputModel investmentCategoryObj = new Gson().fromJson(response.body(), InvestmentCategoryOutputModel.class);
var investmentCategoryId = investmentCategoryObj.Id;
Questionnaire responses
Action: Fetch the questionnaire, resolve response IDs and calculate risk.
Keep / expected result: Keep responseIds and the calculated risk entry.
Display the active questionnaire and collect selectedResponseCodes from the client answers across all sections. This example uses discrete choices. If the questionnaire requires numeric response values, use the applicable schema and include ResponseValue when saving those answers. Resolve those codes to IDs in that questionnaire. Required answers and suitability rules are validated by the API; do not assume a fixed version or question order:
var activeQuestionnaire = await httpClient.GetFromJsonAsync<QuestionnaireOutputModel>($"api/v1/questionnaires/business-line-id/{businessLineVorsorge.Id}/active");
var availableResponses = activeQuestionnaire.Sections.SelectMany(s => s.Questions).SelectMany(q => q.Responses).ToList();
// selectedResponseCodes comes from the client answers to the displayed questionnaire.
var responseIds = selectedResponseCodes.Select(code => availableResponses.Single(r => r.Code == code).Id).ToArray();
var responsesCsv = string.Join(",", responseIds);
var risk = await httpClient.GetFromJsonAsync<RiskCategorizationOutputModel>($"api/v1/risk-categorizations/business-line-id/{businessLineVorsorge.Id}/calculate-risk?csv-response-ids={Uri.EscapeDataString(responsesCsv)}");
HttpRequest questionnaireRequest = HttpRequest.newBuilder()
.uri(Settings.wmsUri("questionnaires/business-line-id/" + businessLineId + "/active"))
.timeout(Duration.ofMinutes(1))
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN).GET().build();
var questionnaireResponse = httpClient.send(questionnaireRequest, BodyHandlers.ofString());
Api.requireSuccess(questionnaireResponse);
QuestionnaireOutputModel activeQuestionnaire = new Gson().fromJson(questionnaireResponse.body(), QuestionnaireOutputModel.class);
var availableResponses = activeQuestionnaire.Sections.stream()
.flatMap(s -> s.Questions.stream()).flatMap(q -> q.Responses.stream()).toList();
// selectedResponseCodes comes from the client answers to the displayed questionnaire.
var responseIds = selectedResponseCodes.stream().map(code -> availableResponses.stream()
.filter(r -> code.equals(r.Code)).findFirst()
.orElseThrow(() -> new IllegalArgumentException("Response not in the active questionnaire")).Id).toList();
var responsesCsv = responseIds.stream().map(String::valueOf).collect(java.util.stream.Collectors.joining(","));
HttpRequest riskRequest = HttpRequest.newBuilder()
.uri(Settings.wmsUri("risk-categorizations/business-line-id/" + businessLineId
+ "/calculate-risk?csv-response-ids=" + java.net.URLEncoder.encode(responsesCsv, StandardCharsets.UTF_8)))
.timeout(Duration.ofMinutes(1))
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN).GET().build();
var riskResponse = httpClient.send(riskRequest, BodyHandlers.ofString());
Api.requireSuccess(riskResponse);
RiskCategorizationOutputModel riskCategorizationObj = new Gson().fromJson(riskResponse.body(), RiskCategorizationOutputModel.class);
Obtaining investment proposals
Action: Retrieve the proposals and collect the client selection.
Keep / expected result: Keep the chosen proposalId and any required deviation-reason IDs.
var proposals = await httpClient.GetFromJsonAsync<List<ProposalOutputModel>>($"api/v1/proposals/investment-category-id/{investmentCategory3A.Id}/risk-id/{risk.Id}");
// The client chooses proposalId from the displayed proposals.
var proposalSelectedByUser = proposals.Single(x => x.Id == proposalId);
HttpRequest proposalRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("proposals/investment-category-id/" + investmentCategoryId + "/risk-id/" + riskCategorizationObj.Id))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
.header("Content-Type", "application/json")
.build();
response = httpClient.send(proposalRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Type listType = new TypeToken<ArrayList<ProposalOutputModel>>(){}.getType();
List<ProposalOutputModel> proposalsListObj = new Gson().fromJson(response.body(), listType);
// proposalId is selected by the client from the displayed proposals.
var proposalSelectedByUser = proposalsListObj.stream().filter(x -> x.Id == proposalId)
.findFirst().orElseThrow(() -> new IllegalArgumentException("Select a displayed proposal"));
public class ReasonToChangeProposalResponseOutputModel
{
public long Id { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public int Order { get; set; }
}
public class ReasonToChangeProposalOutputModel
{
public long Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public bool IsDeleted { get; set; }
public IList<ReasonToChangeProposalResponseOutputModel> Responses { get; set; }
}
import java.util.List;
import java.util.Map;
import com.google.gson.annotations.SerializedName;
public class ReasonToChangeProposalResponseOutputModel {
@SerializedName("id")
public long Id;
@SerializedName("code")
public String Code;
@SerializedName("description")
public String Description;
@SerializedName("order")
public int Order;
}
public class ReasonToChangeProposalOutputModel {
@SerializedName("id")
public long Id;
@SerializedName("code")
public String Code;
@SerializedName("name")
public String Name;
@SerializedName("isDeleted")
public boolean IsDeleted;
@SerializedName("responses")
public List<ReasonToChangeProposalResponseOutputModel> Responses;
}
var reasonsToChangeProposal = await httpClient.GetFromJsonAsync<ReasonToChangeProposalOutputModel>($"api/v1/reason-to-change-proposed-proposal/investment-category-id/{investmentCategory3A.Id}/active");
HttpRequest reasonToChangeRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("reason-to-change-proposed-proposal/investment-category-id/" + investmentCategoryId + "/active"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
.header("Content-Type", "application/json")
.build();
response = httpClient.send(reasonToChangeRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
ReasonToChangeProposalOutputModel reasonToChangeProposalObj = new Gson().fromJson(response.body(), ReasonToChangeProposalOutputModel.class);