Modifier la stratégie

Comment suivre ce chapitre

Entrées : Un portefeuille existant admissible, son état actuel et la nouvelle proposition choisie.

Étapes et résultat attendu : Choisissez la proposition et préparez le contrat avant l’envoi. Conservez l’identifiant de l’ordre de modification et suivez-le comme dans Ouvrir un portefeuille.

Alternatives et actions ultérieures : Vérifiez HasPendingOrder et le résultat API avant de permettre une autre modification. L’API reste l’autorité pour l’admissibilité.

Code source des DTO : PortfolioOrderOutputModel, PortfolioOutputModel, ProposalOutputModel, ReasonToChangeProposalOutputModel

Les variables affectées dans un bloc précédent sont réutilisées dans les suivants. Les saisies du client proviennent de votre interface. Conservez les identifiants et les valeurs de référence dans le contexte du client ; ne recréez pas ce contexte à chaque étape.

Les exemples utilisent des classes utilitaires communes. Consultez Classes utilitaires communes : code source C# et Java complet avant de copier un exemple.
Prérequis : Un portfolioId sélectionné, un token CLIENT ou ADMIN autorisé et aucun ordre en attente. Vérifiez le portefeuille et choisissez une proposition alternative.
À conserver pour la suite : Un nouvel ordre de modification. Suivez son état avant de permettre une autre modification.
Le client peut demander un autre modèle si le produit, le cycle de vie du portefeuille et les ordres en attente le permettent. Si le risque du nouveau modèle ne correspond pas au profil du client, fournissez les identifiants de réponse des motifs d’écart.

Important

Vérifiez le cycle de vie et les ordres en attente avant de proposer une modification. L’API reste l’autorité pour l’admissibilité.

Sélectionner une proposition

Entrées : Le snapshot actuel et la sélection du client.
Action : Identifiez la proposition admissible pour la modification.
À conserver / résultat attendu : Conservez selectedProposalId et les motifs d’écart requis.
Récupérez le snapshot actuel et les propositions de la catégorie du portefeuille. Laissez le client choisir selectedProposalId dans alternativeProposals et recueillez selectedReasonIds si nécessaire. Ces décisions appartiennent au client ; ne choisissez pas automatiquement la première proposition ou le premier motif.
								
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.

Préparer le contrat

Entrées : La proposition choisie et les données du client.
Action : Préparez le contrat de changement de stratégie.
À conserver / résultat attendu : Conservez les octets PDF ou contractPath comme dans Ouvrir un portefeuille.
Préparez un nouveau contrat pour selectedProposalId, sans réutiliser celui de l’ouverture. Suivez Préparer le contrat avec les données actuelles et la nouvelle proposition dans formFields. Définissez contractPath avec le PDF complet envoyé ici. En C#, si le service PDF retourne pdfContractAsByteArray, sauvegardez ces octets dans un fichier distinct et utilisez ce chemin ; n’envoyez pas le modèle vide.

Envoyer l’ordre

Entrées : portfolioId, selectedProposalId, motifs d’écart et contrat PDF.
Action : Envoyez l’ordre de modification.
À conserver / résultat attendu : Conservez modificationOrderId et suivez l’ordre avant de considérer le changement comme terminé.
L’exemple envoie la demande avec le nouvel identifiant de proposition choisi par le client. L’API enregistre un ordre de modification ; la stratégie change après le traitement ultérieur.
								
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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;

Après réussite, conservez modificationOrderId et lisez son état par la procédure de suivi de l’ordre. Actualisez le portefeuille pour observer HasPendingOrder pendant le traitement.
Quand HasPendingOrder est TRUE, aucune modification de stratégie n’est autorisée.
Actualisez le portefeuille et l’état après traitement. Continuez uniquement si HasPendingOrder est false et si le cycle de vie permet une autre modification ; l’API en vérifie toujours l’admissibilité.

Reconnaître le résultat

Extrait illustratif d’une réponse de modification. Conservez ce nouvel identifiant ; le succès HTTP ne signifie pas l’achèvement du changement d’investissement.

{"id":457,"portfolioId":789,"orderStatusId":1}

Le catalogue des DTO contient les autres propriétés de la réponse. Traitez une demande échouée avant de poursuivre ; consultez Gestion des erreurs.