Plan Modification
How to follow this chapter
Inputs: An existing eligible portfolio, its latest state and the newly selected proposal.
Steps and expected result: Choose the proposal and prepare the contract before submission. Keep the modification order ID and track it as described in Plan creation.
Alternative or later actions: Check HasPendingOrder and the API result before allowing another change; the service remains authoritative for eligibility.
DTO source: PortfolioOrderOutputModel, PortfolioOutputModel, ProposalOutputModel, ReasonToChangeProposalOutputModel
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.
Attention
Selecting proposal
Action: Resolve the proposal permitted for the requested change.
Keep / expected result: Keep selectedProposalId and any required deviation reasons.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var clientPortfolio = await httpClient.GetFromJsonAsync<PortfolioOutputModel>($"api/v1/user-portfolios/portfolio-id/{portfolioId}?portfolio-view-as=Snapshot");
var url = $"api/v1/proposals/investment-category-id/{clientPortfolio.InvestmentCategoryId}";
var proposals = await httpClient.GetFromJsonAsync<List<ProposalOutputModel>>(url);
var alternativeProposals = proposals.Where(x => x.Id != clientPortfolio.ProposalId).ToList();
HttpRequest portfolioRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("user-portfolios/portfolio-id/" + portfolioId + "?portfolio-view-as=Snapshot"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.build();
var response = httpClient.send(portfolioRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
PortfolioOutputModel clientPortfolio = new Gson().fromJson(response.body(), PortfolioOutputModel.class);
// Alternative proposals list
HttpRequest proposalsRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("proposals/investment-category-id/" + clientPortfolio.InvestmentCategoryId))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.build();
response = httpClient.send(proposalsRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Type listType = new TypeToken<ArrayList<ProposalOutputModel>>(){}.getType();
List<ProposalOutputModel> proposalObjs = new Gson().fromJson(response.body(), listType);
var alternativeProposals = proposalObjs.stream().filter(x -> x.Id != clientPortfolio.ProposalId).toList();
// selectedProposalId is chosen by the client from alternativeProposals.
Preparing the contract
Action: Prepare the contract for the strategy change.
Keep / expected result: Keep the PDF bytes or contractPath, as in Plan creation.
Submitting the order
Action: Submit the modification order.
Keep / expected result: Keep modificationOrderId and track the order before treating the change as complete.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Please note:
// Provide the completed contract required for this modification.
// The client chooses selectedProposalId and selectedReasonIds.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var currentPortfolio = await httpClient.GetFromJsonAsync<PortfolioOutputModel>($"api/v1/user-portfolios/portfolio-id/{portfolioId}?portfolio-view-as=Snapshot");
if (currentPortfolio.HasPendingOrder)
{
throw new InvalidOperationException("A portfolio order is pending");
}
var reasonToChangeProposalUrl = $"api/v1/reason-to-change-proposed-proposal/investment-category-id/{currentPortfolio.InvestmentCategoryId}/active";
var reasonToChangeProposal = await httpClient.GetFromJsonAsync<ReasonToChangeProposalOutputModel>(reasonToChangeProposalUrl);
var orderPortfolioModificationInputModel = new
{
PortfolioId = portfolioId,
// Assume this is the new portfolio model selected by client
ProposalId = selectedProposalId,
// Assume this is reason to change proposal selected by client
ReasonToChangeProposalResponsesIds = selectedReasonIds
};
// contractPath points to the completed contract for this modification.
var byteArray = await File.ReadAllBytesAsync(contractPath);
using var httpContent = GuideHttp.CreateMultipartContent(orderPortfolioModificationInputModel, byteArray);
var httpResponseMessage = await httpClient.PostAsync("api/v1/user-portfolio-orders/modification", httpContent);
httpResponseMessage.EnsureSuccessStatusCode();
var modificationOrder = JsonConvert.DeserializeObject<PortfolioOrderOutputModel>(await httpResponseMessage.Content.ReadAsStringAsync());
var modificationOrderId = modificationOrder.Id;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Please note:
// Provide the completed contract required for this modification.
// The client chooses selectedProposalId and selectedReasonIds.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
HttpRequest reasonToChangeRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("reason-to-change-proposed-proposal/investment-category-id/" + clientPortfolio.InvestmentCategoryId + "/active"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.build();
response = httpClient.send(reasonToChangeRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
ReasonToChangeProposalOutputModel reasonToChangeProposalObj = new Gson().fromJson(response.body(), ReasonToChangeProposalOutputModel.class);
// selectedReasonIds contains the reasons selected by the client.
JsonObject orderPortfolioModificationInputModel = new JsonObject();
orderPortfolioModificationInputModel.addProperty("PortfolioId", portfolioId);
orderPortfolioModificationInputModel.addProperty("ProposalId", selectedProposalId);
orderPortfolioModificationInputModel.add("ReasonToChangeProposalResponsesIds", new Gson().toJsonTree(selectedReasonIds));
// contractPath points to the completed contract for this modification.
var contractBytes = Files.readAllBytes(contractPath);
MultiPartBodyPublisher multipartBody = new MultiPartBodyPublisher()
.addPart("jsonPayload", orderPortfolioModificationInputModel.toString())
.addPart("userContracts", () -> new ByteArrayInputStream(contractBytes), "Contract.pdf", "application/pdf");
HttpRequest planChangeRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("user-portfolio-orders/modification"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "multipart/form-data; boundary=" + multipartBody.getBoundary())
.POST(multipartBody.build())
.build();
response = httpClient.send(planChangeRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
PortfolioOrderOutputModel modificationOrder = new Gson().fromJson(response.body(), PortfolioOrderOutputModel.class);
var modificationOrderId = modificationOrder.Id;
Recognize the result
Illustrative modification-order response projection. Keep this new order ID; do not treat HTTP success as completion of the investment change.
{"id":457,"portfolioId":789,"orderStatusId":1}The DTO catalog contains the remaining response properties. A failed request must be handled before advancing to the next step; see Error handling.