refactoring the command
All checks were successful
Dotnet build and test / build (pull_request) Successful in 8m54s
All checks were successful
Dotnet build and test / build (pull_request) Successful in 8m54s
This commit is contained in:
parent
fc820362b9
commit
6b4e52e7ce
15 changed files with 459 additions and 188 deletions
101
doc/agent-playbook.md
Normal file
101
doc/agent-playbook.md
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Playbook d'usage des agents IA (Yavsc)
|
||||||
|
|
||||||
|
Ce playbook normalise l'usage de Copilot, Plan et Explore dans le depot.
|
||||||
|
Il privilegie des sorties verifiables: fichiers, commandes tests, risques.
|
||||||
|
|
||||||
|
## Quand utiliser quel agent
|
||||||
|
|
||||||
|
- Plan: quand la tache est ambigue, transverse ou risquee.
|
||||||
|
- Explore: quand il faut cartographier rapidement des zones du code.
|
||||||
|
- Copilot: quand les specifications sont claires et localisees.
|
||||||
|
|
||||||
|
## Prompt type (base)
|
||||||
|
|
||||||
|
Utiliser ce squelette avant toute tache non triviale:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Contexte: <projet/fichier/fonction>
|
||||||
|
Objectif: <resultat observable>
|
||||||
|
Contraintes: <style, archi, perimetre>
|
||||||
|
Verification: <tests exacts a lancer>
|
||||||
|
Sortie attendue: <fichiers modifies + risques>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4 scenarios de reference
|
||||||
|
|
||||||
|
## 1) Explorer un bounded context
|
||||||
|
|
||||||
|
Intention:
|
||||||
|
- Comprendre ou implementer un changement dans un BC sans regression laterale.
|
||||||
|
|
||||||
|
Prompt minimal:
|
||||||
|
```text
|
||||||
|
Explore le BC <nom> avec profondeur medium.
|
||||||
|
Retour: composants touches, points d'entree, tests existants et risques.
|
||||||
|
```
|
||||||
|
|
||||||
|
Preuves attendues:
|
||||||
|
- Carte des fichiers a modifier.
|
||||||
|
- Test(s) smoke/mandatory proposes.
|
||||||
|
|
||||||
|
## 2) Ajouter un smoke test
|
||||||
|
|
||||||
|
Intention:
|
||||||
|
- Couvrir rapidement un endpoint ou une route publique.
|
||||||
|
|
||||||
|
Prompt minimal:
|
||||||
|
```text
|
||||||
|
Propose un smoke test pour <route/endpoint> dans le projet de test approprie.
|
||||||
|
Respecte les conventions de doc/testing.md.
|
||||||
|
```
|
||||||
|
|
||||||
|
Preuves attendues:
|
||||||
|
- Fichier test cree/modifie.
|
||||||
|
- Commande precise pour executer le test cible.
|
||||||
|
|
||||||
|
## 3) Corriger une regression backend API
|
||||||
|
|
||||||
|
Intention:
|
||||||
|
- Corriger un bug sans casser un flux voisin.
|
||||||
|
|
||||||
|
Prompt minimal:
|
||||||
|
```text
|
||||||
|
Planifie puis implemente un fix de <symptome> dans <projet>.
|
||||||
|
Ajoute/ajuste un test NonRegression rouge puis vert.
|
||||||
|
```
|
||||||
|
|
||||||
|
Preuves attendues:
|
||||||
|
- Explication cause racine.
|
||||||
|
- Test non-regression associe.
|
||||||
|
- Commande d'execution et resultat attendu.
|
||||||
|
|
||||||
|
## 4) Tracer un flux PostIt/OIDC
|
||||||
|
|
||||||
|
Intention:
|
||||||
|
- Localiser une cassure d'authentification entre client et serveur.
|
||||||
|
|
||||||
|
Prompt minimal:
|
||||||
|
```text
|
||||||
|
Cartographie le flux OIDC PostIt: entrypoints, callback, stockage token,
|
||||||
|
refresh. Donne points de rupture probables et tests/verification proposes.
|
||||||
|
```
|
||||||
|
|
||||||
|
Preuves attendues:
|
||||||
|
- Liste ordonnee des etapes du flux.
|
||||||
|
- Fichiers critiques.
|
||||||
|
- Hypotheses testables.
|
||||||
|
|
||||||
|
## Anti-patterns a eviter
|
||||||
|
|
||||||
|
- Prompt sans objectif verifiable.
|
||||||
|
- Demande trop large sans perimetre de fichiers.
|
||||||
|
- Validation basee uniquement sur "ca semble correct".
|
||||||
|
- Pas de lien entre changement et niveau de test.
|
||||||
|
|
||||||
|
## Gate PR minimale (agent-assiste)
|
||||||
|
|
||||||
|
Avant validation:
|
||||||
|
- Impact architecture explicite.
|
||||||
|
- Rationale de choix agent explicite.
|
||||||
|
- Test(s) executes et justifies.
|
||||||
|
- Risques residuels documentes.
|
||||||
73
doc/onboarding-agents.md
Normal file
73
doc/onboarding-agents.md
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Onboarding guide: agents IA + architecture + tests
|
||||||
|
|
||||||
|
Ce guide est optimise pour accelerer la prise en main des agents IA
|
||||||
|
(Copilot, Plan, Explore) dans Yavsc, avec une verification rapide
|
||||||
|
par les tests.
|
||||||
|
|
||||||
|
## Resultat attendu
|
||||||
|
|
||||||
|
A la fin du parcours, un contributeur doit pouvoir:
|
||||||
|
- Identifier les projets impactes par une modification.
|
||||||
|
- Choisir l'agent adapte a l'intention de travail.
|
||||||
|
- Produire une proposition de changement verifiable par les tests.
|
||||||
|
|
||||||
|
## Parcours en 3 modules
|
||||||
|
|
||||||
|
## Module A - Comprendre le terrain (30-45 min)
|
||||||
|
|
||||||
|
Objectif: acquerir une lecture fiable de l'architecture.
|
||||||
|
|
||||||
|
1. Lire [README.md](../README.md) puis [Architecture.md](Architecture.md).
|
||||||
|
2. Lire [architecture/decoupage-organisation.md](architecture/decoupage-organisation.md).
|
||||||
|
3. Selon le domaine:
|
||||||
|
- Backend/API: [architecture/workflow-multi-parties.md](architecture/workflow-multi-parties.md)
|
||||||
|
- PostIt: [architecture/postit.md](architecture/postit.md) puis [architecture/postit-oidc.md](architecture/postit-oidc.md)
|
||||||
|
|
||||||
|
Definition of done:
|
||||||
|
- Expliquer en 5 phrases quelles couches sont touchees.
|
||||||
|
- Citer le ou les points d'entree applicatifs a verifier.
|
||||||
|
|
||||||
|
## Module B - Boucle tests rapide (20-30 min)
|
||||||
|
|
||||||
|
Objectif: verifier rapidement sans lancer toute la suite.
|
||||||
|
|
||||||
|
1. Lire [testing.md](testing.md).
|
||||||
|
2. Lancer les smoke tests d'abord, puis mandatory selon le projet.
|
||||||
|
3. N'elargir au test complet que si le scope depasse le BC touche.
|
||||||
|
|
||||||
|
Definition of done:
|
||||||
|
- Fournir la commande test executee.
|
||||||
|
- Expliquer pourquoi ce niveau de test est suffisant.
|
||||||
|
|
||||||
|
## Module C - Usage agentique en production (30-40 min)
|
||||||
|
|
||||||
|
Objectif: utiliser les agents comme accelerateurs, pas comme boites noires.
|
||||||
|
|
||||||
|
1. Plan: decomposer la tache en etapes verifiables.
|
||||||
|
2. Explore: collecter le contexte code/doc precise.
|
||||||
|
3. Copilot: implementer localement et verifier.
|
||||||
|
|
||||||
|
Regles:
|
||||||
|
- Toujours donner un contexte explicite (fichier, but, contrainte).
|
||||||
|
- Demander des preuves observables (fichiers modifies, tests, risques).
|
||||||
|
- Refuser toute sortie non verifiable.
|
||||||
|
|
||||||
|
Definition of done:
|
||||||
|
- Une tache simple est livree avec:
|
||||||
|
- Plan
|
||||||
|
- Changement local
|
||||||
|
- Preuve par test
|
||||||
|
|
||||||
|
## Routine continue (sans echeance fixe)
|
||||||
|
|
||||||
|
Rituels recommandes:
|
||||||
|
- Hebdo: revue des prompts qui ont bien fonctionne.
|
||||||
|
- Mensuel: mise a jour du present guide et du playbook.
|
||||||
|
- A chaque incident: ajouter un anti-pattern dans le playbook.
|
||||||
|
|
||||||
|
## Check-list de validation
|
||||||
|
|
||||||
|
- Le changement indique son impact architecture.
|
||||||
|
- Le choix de l'agent est justifie.
|
||||||
|
- La preuve test est incluse.
|
||||||
|
- Les risques residuels sont explicitement listes.
|
||||||
|
|
@ -24,12 +24,12 @@ public class BillingCommandPageViewModelTests
|
||||||
new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
|
new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
|
||||||
client) as RdvViewModel;
|
client) as RdvViewModel;
|
||||||
|
|
||||||
vm.EventDate = DateTime.Parse("2026-09-02 14:30");
|
vm!.EventDate = DateTime.Parse("2026-09-02 14:30");
|
||||||
vm.Reason = "Point de cadrage";
|
vm!.Reason = "Point de cadrage";
|
||||||
vm.Address = "1 rue du Test";
|
vm!.Address = "1 rue du Test";
|
||||||
vm.Latitude = 48.8566;
|
vm!.Latitude = 48.8566;
|
||||||
vm.Longitude = 2.3522;
|
vm!.Longitude = 2.3522;
|
||||||
vm.Consent = true;
|
vm!.Consent = true;
|
||||||
|
|
||||||
await vm.SubmitCommand.ExecuteAsync(null);
|
await vm.SubmitCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
|
|
@ -56,10 +56,7 @@ public class BillingCommandPageViewModelTests
|
||||||
new ActivityUserDisplayItem { PerformerId = "perf-2", UserName = "Bob" },
|
new ActivityUserDisplayItem { PerformerId = "perf-2", UserName = "Bob" },
|
||||||
client);
|
client);
|
||||||
|
|
||||||
await vm.SubmitCommand.ExecuteAsync(null);
|
Assert.Null(vm);
|
||||||
|
|
||||||
Assert.Null(api.LastPath);
|
|
||||||
Assert.Contains("n'est pas encore pris en charge", vm.StatusMessage, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -74,12 +71,12 @@ public class BillingCommandPageViewModelTests
|
||||||
new ActivityInfo { Code = "dev", Name = "Développement" },
|
new ActivityInfo { Code = "dev", Name = "Développement" },
|
||||||
new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
|
new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
|
||||||
client) as RdvViewModel;
|
client) as RdvViewModel;
|
||||||
vm.EventDate = DateTime.Parse("2026-09-02 14:30");
|
vm!.EventDate = DateTime.Parse("2026-09-02 14:30");
|
||||||
vm.Reason = "Point de cadrage";
|
vm!.Reason = "Point de cadrage";
|
||||||
vm.Address = "1 rue du Test";
|
vm!.Address = "1 rue du Test";
|
||||||
vm.Latitude = 0;
|
vm!.Latitude = null;
|
||||||
vm.Longitude = 0;
|
vm!.Longitude = null;
|
||||||
vm.Consent = true;
|
vm!.Consent = true;
|
||||||
|
|
||||||
await vm.SubmitCommand.ExecuteAsync(null);
|
await vm.SubmitCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
|
|
@ -107,10 +104,10 @@ public class BillingCommandPageViewModelTests
|
||||||
new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
|
new ActivityUserDisplayItem { PerformerId = "perf-1", UserName = "Alice" },
|
||||||
client) as RdvViewModel;
|
client) as RdvViewModel;
|
||||||
|
|
||||||
await vm.UseCurrentLocationCommand.ExecuteAsync(null);
|
await vm!.UseCurrentLocationCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
Assert.Equal(48.8566, vm.Latitude);
|
Assert.Equal(48.8566, vm!.Latitude);
|
||||||
Assert.Equal(2.3522, vm.Longitude);
|
Assert.Equal(2.3522, vm!.Longitude);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -136,12 +133,12 @@ public class BillingCommandPageViewModelTests
|
||||||
new ActivityInfo { Code = "brush", Name = "Brush" },
|
new ActivityInfo { Code = "brush", Name = "Brush" },
|
||||||
new ActivityUserDisplayItem { PerformerId = "perf-2", UserName = "Bob" },
|
new ActivityUserDisplayItem { PerformerId = "perf-2", UserName = "Bob" },
|
||||||
client) as BrushViewModel;
|
client) as BrushViewModel;
|
||||||
vm.EventDate = DateTime.Parse("2026-09-02 14:30");
|
vm!.EventDate = DateTime.Parse("2026-09-02 14:30");
|
||||||
vm.Address = "1 rue du Test";
|
vm!.Address = "1 rue du Test";
|
||||||
vm.Latitude = 48.8566;
|
vm!.Latitude = 48.8566;
|
||||||
vm.Longitude = 2.3522;
|
vm!.Longitude = 2.3522;
|
||||||
vm.Consent = true;
|
vm!.Consent = true;
|
||||||
vm.AdditionalInfo = "Prévoir shampoing";
|
vm!.AdditionalInfo = "Prévoir shampoing";
|
||||||
|
|
||||||
await vm.InitializeAsync();
|
await vm.InitializeAsync();
|
||||||
vm.SelectedPrestation = vm.AvailablePrestations[1];
|
vm.SelectedPrestation = vm.AvailablePrestations[1];
|
||||||
|
|
@ -170,17 +167,17 @@ public class BillingCommandPageViewModelTests
|
||||||
.CreateCommandPageViewModel(
|
.CreateCommandPageViewModel(
|
||||||
new ActivityInfo { Code = "mbrush", Name = "MBrush" },
|
new ActivityInfo { Code = "mbrush", Name = "MBrush" },
|
||||||
new ActivityUserDisplayItem { PerformerId = "perf-3", UserName = "Cara" },
|
new ActivityUserDisplayItem { PerformerId = "perf-3", UserName = "Cara" },
|
||||||
client) as PostIt.ViewModels.Commands.BrushViewModel;
|
client) as MBrushViewModel;
|
||||||
vm.EventDate = DateTime.Parse("2026-09-03 10:00");
|
vm!.EventDate = DateTime.Parse("2026-09-03 10:00");
|
||||||
vm.Address = "2 rue du Test";
|
vm!.Address = "2 rue du Test";
|
||||||
vm.Latitude = 48.8567;
|
vm!.Latitude = 48.8567;
|
||||||
vm.Longitude = 2.3523;
|
vm!.Longitude = 2.3523;
|
||||||
vm.Consent = true;
|
vm!.Consent = true;
|
||||||
|
|
||||||
await vm.InitializeAsync();
|
await vm.InitializeAsync();
|
||||||
vm.MultiPrestations[0].IsSelected = true;
|
vm!.MultiPrestations[0].IsSelected = true;
|
||||||
vm.MultiPrestations[1].IsSelected = true;
|
vm!.MultiPrestations[1].IsSelected = true;
|
||||||
await vm.SubmitCommand.ExecuteAsync(null);
|
await vm!.SubmitCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
Assert.Equal("https://business.example/api/v1/billing/MBrush", api.LastPath);
|
Assert.Equal("https://business.example/api/v1/billing/MBrush", api.LastPath);
|
||||||
using var json = JsonDocument.Parse(JsonSerializer.Serialize(api.LastBody));
|
using var json = JsonDocument.Parse(JsonSerializer.Serialize(api.LastBody));
|
||||||
|
|
@ -208,7 +205,7 @@ public class BillingCommandPageViewModelTests
|
||||||
new ActivityInfo { Code = "brush", Name = "Brush" },
|
new ActivityInfo { Code = "brush", Name = "Brush" },
|
||||||
new ActivityUserDisplayItem { PerformerId = "perf-2", UserName = "Bob" },
|
new ActivityUserDisplayItem { PerformerId = "perf-2", UserName = "Bob" },
|
||||||
client) as BrushViewModel;
|
client) as BrushViewModel;
|
||||||
await vm.InitializeAsync(new BillingQueryDetailsDto
|
await vm!.InitializeAsync(new BillingQueryDetailsDto
|
||||||
{
|
{
|
||||||
Id = 77,
|
Id = 77,
|
||||||
BillingCode = "Brush",
|
BillingCode = "Brush",
|
||||||
|
|
@ -228,9 +225,9 @@ public class BillingCommandPageViewModelTests
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
vm.SelectedPrestation = vm.AvailablePrestations[1];
|
vm!.SelectedPrestation = vm!.AvailablePrestations[1];
|
||||||
vm.AdditionalInfo = "Note mise à jour";
|
vm!.AdditionalInfo = "Note mise à jour";
|
||||||
await vm.SubmitCommand.ExecuteAsync(null);
|
await vm!.SubmitCommand.ExecuteAsync(null);
|
||||||
|
|
||||||
Assert.Equal(HttpMethod.Put, api.LastMethod);
|
Assert.Equal(HttpMethod.Put, api.LastMethod);
|
||||||
Assert.Equal("https://business.example/api/v1/billing/Brush/77", api.LastPath);
|
Assert.Equal("https://business.example/api/v1/billing/Brush/77", api.LastPath);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace PostIt.Helpers;
|
||||||
|
|
||||||
public static class FormHelpers
|
public static class FormHelpers
|
||||||
{
|
{
|
||||||
public static BillingCommandPageViewModel
|
public static BillingCommandPageViewModel?
|
||||||
CreateCommandPageViewModel(
|
CreateCommandPageViewModel(
|
||||||
this CommandFormSummary form,
|
this CommandFormSummary form,
|
||||||
ActivityInfo activity,
|
ActivityInfo activity,
|
||||||
|
|
@ -15,25 +15,30 @@ public static class FormHelpers
|
||||||
BillingApiClient billingClient)
|
BillingApiClient billingClient)
|
||||||
{
|
{
|
||||||
|
|
||||||
string formVMName = form.ActionName + "ViewModel";
|
string namespacePrefix = typeof(PostIt.ViewModels.Commands.RdvViewModel).Namespace + ".";
|
||||||
|
|
||||||
string formOnActivityVMName = activity.Code + formVMName;
|
string formVMName = form.ActionName + "ViewModel";
|
||||||
|
|
||||||
var vmType = Type.GetType(formOnActivityVMName);
|
string formOnActivityVMName = activity.Code + formVMName + "ViewModel";
|
||||||
|
|
||||||
|
var vmType = Type.GetType(namespacePrefix +formOnActivityVMName);
|
||||||
if (vmType == null)
|
if (vmType == null)
|
||||||
{
|
{
|
||||||
vmType = Type.GetType(formVMName);
|
vmType = Type.GetType(namespacePrefix + formVMName);
|
||||||
}
|
}
|
||||||
if (vmType == null)
|
if (vmType == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"Cannot find type '{formOnActivityVMName}' or '{formVMName}'");
|
Console.Error.WriteLine(
|
||||||
|
$"! Cannot find type '{formOnActivityVMName}' or '{formVMName}'");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
if (!typeof(BillingCommandPageViewModel).IsAssignableFrom(vmType))
|
if (!typeof(BillingCommandPageViewModel).IsAssignableFrom(vmType))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"The type '{formOnActivityVMName}' or '{formVMName}' is not a BillingCommandPageViewModel");
|
Console.Error.WriteLine($"! The type '{formOnActivityVMName}' or '{formVMName}' is not a BillingCommandPageViewModel");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var vm = Activator.CreateInstance(vmType);
|
var vm = Activator.CreateInstance(vmType, activity, performer, form, billingClient);
|
||||||
|
|
||||||
if (vm == null)
|
if (vm == null)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ public class ViewLocator : IDataTemplate
|
||||||
HomePageViewModel => services.GetRequiredService<HomePage>(),
|
HomePageViewModel => services.GetRequiredService<HomePage>(),
|
||||||
ActivitiesPageViewModel => services.GetRequiredService<ActivitiesPage>(),
|
ActivitiesPageViewModel => services.GetRequiredService<ActivitiesPage>(),
|
||||||
CommandFormsPageViewModel => services.GetRequiredService<CommandFormsPage>(),
|
CommandFormsPageViewModel => services.GetRequiredService<CommandFormsPage>(),
|
||||||
|
BrushViewModel => services.GetRequiredService<BrushPage>(),
|
||||||
RdvViewModel => services.GetRequiredService<RdvPage>(),
|
RdvViewModel => services.GetRequiredService<RdvPage>(),
|
||||||
SignaturePageViewModel => services.GetRequiredService<SignaturePage>(),
|
SignaturePageViewModel => services.GetRequiredService<SignaturePage>(),
|
||||||
AddCircleMemberDialogViewModel => services.GetRequiredService<AddCircleMemberDialog>(),
|
AddCircleMemberDialogViewModel => services.GetRequiredService<AddCircleMemberDialog>(),
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ public partial class BillingQueriesPageViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
var details = await _billingClient.GetQueryAsync(Form.ActionName, SelectedQuery.Id).ConfigureAwait(true);
|
var details = await _billingClient.GetQueryAsync(Form.ActionName, SelectedQuery.Id).ConfigureAwait(true);
|
||||||
var vm = Form.CreateCommandPageViewModel(Activity, Performer, _billingClient);
|
var vm = Form.CreateCommandPageViewModel(Activity, Performer, _billingClient);
|
||||||
await vm.InitializeAsync(details).ConfigureAwait(true);
|
await vm!.InitializeAsync(details).ConfigureAwait(true);
|
||||||
await app.PushPageAsync(vm).ConfigureAwait(true);
|
await app.PushPageAsync(vm).ConfigureAwait(true);
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
|
catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ public partial class CommandFormsPageViewModel : ViewModelBase
|
||||||
|
|
||||||
var vm = SelectedForm.CreateCommandPageViewModel(
|
var vm = SelectedForm.CreateCommandPageViewModel(
|
||||||
Activity, Performer, _billingClient);
|
Activity, Performer, _billingClient);
|
||||||
await vm.InitializeAsync();
|
await vm!.InitializeAsync();
|
||||||
await app.PushPageAsync(vm);
|
await app.PushPageAsync(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ using Yavsc.Models.Billing;
|
||||||
|
|
||||||
namespace PostIt.ViewModels;
|
namespace PostIt.ViewModels;
|
||||||
|
|
||||||
public abstract partial class BillingCommandPageViewModel : ViewModelBase
|
public abstract partial class BillingCommandPageViewModel : RemoteViewModelBase
|
||||||
{
|
{
|
||||||
protected readonly BillingApiClient _billingClient;
|
protected readonly BillingApiClient _billingClient;
|
||||||
|
|
||||||
|
|
@ -46,24 +46,11 @@ public abstract partial class BillingCommandPageViewModel : ViewModelBase
|
||||||
public string Title => Form.Title;
|
public string Title => Form.Title;
|
||||||
public string PerformerLabel => Performer.UserName;
|
public string PerformerLabel => Performer.UserName;
|
||||||
public string ActivityLabel => Activity.Name;
|
public string ActivityLabel => Activity.Name;
|
||||||
public bool IsSupported => IsRdv || IsBrush || IsMultiBrush;
|
public virtual bool IsSupported => true;
|
||||||
public bool IsRdv => string.Equals(Form.ActionName, BillingCodes.Rdv, StringComparison.Ordinal);
|
|
||||||
public bool IsBrush => string.Equals(Form.ActionName, BillingCodes.Brush, StringComparison.Ordinal);
|
|
||||||
public bool IsMultiBrush => string.Equals(Form.ActionName, BillingCodes.MBrush, StringComparison.Ordinal);
|
|
||||||
public bool ShowsReason => IsRdv;
|
|
||||||
public bool ShowsAdditionalInfo => IsBrush;
|
|
||||||
public bool ShowsSinglePrestation => IsBrush;
|
|
||||||
public bool ShowsMultiplePrestations => IsMultiBrush;
|
|
||||||
public string BillingRoute => $"/billing/{Form.ActionName}";
|
public string BillingRoute => $"/billing/{Form.ActionName}";
|
||||||
public bool IsEditingExisting => ExistingQueryId.HasValue;
|
public bool IsEditingExisting => ExistingQueryId.HasValue;
|
||||||
public string SubmitLabel => IsEditingExisting ? "Mettre à jour la commande" : "Poster la commande";
|
public string SubmitLabel => IsEditingExisting ? "Mettre à jour la commande" : "Poster la commande";
|
||||||
public string SupportMessage => IsSupported
|
public virtual string SupportMessage => $"Le formulaire {Form.ActionName} n'est pas encore pris en charge dans PostIt.";
|
||||||
? IsRdv
|
|
||||||
? "Complétez les informations du rendez-vous puis postez la commande."
|
|
||||||
: IsBrush
|
|
||||||
? "Choisissez une prestation coiffure puis postez la commande."
|
|
||||||
: "Choisissez une ou plusieurs prestations coiffure puis postez la commande."
|
|
||||||
: $"Le formulaire {Form.ActionName} n'est pas encore pris en charge dans PostIt.";
|
|
||||||
|
|
||||||
public override bool CanNavigateNext
|
public override bool CanNavigateNext
|
||||||
{
|
{
|
||||||
|
|
@ -104,7 +91,7 @@ public abstract partial class BillingCommandPageViewModel : ViewModelBase
|
||||||
|
|
||||||
public async Task InitializeAsync(BillingQueryDetailsDto? existingQuery = null)
|
public async Task InitializeAsync(BillingQueryDetailsDto? existingQuery = null)
|
||||||
{
|
{
|
||||||
|
await LoadAsync();
|
||||||
if (existingQuery is not null)
|
if (existingQuery is not null)
|
||||||
{
|
{
|
||||||
ApplyExistingQuery(existingQuery);
|
ApplyExistingQuery(existingQuery);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ using Yavsc.Models.Haircut;
|
||||||
namespace PostIt.ViewModels.Commands;
|
namespace PostIt.ViewModels.Commands;
|
||||||
public partial class BrushViewModel : RdvViewModel
|
public partial class BrushViewModel : RdvViewModel
|
||||||
{
|
{
|
||||||
|
public override string SupportMessage => "Choisissez une prestation coiffure puis postez la commande.";
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial ObservableCollection<HairPrestationDto> AvailablePrestations { get; set; } = new();
|
public partial ObservableCollection<HairPrestationDto> AvailablePrestations { get; set; } = new();
|
||||||
|
|
@ -19,42 +20,42 @@ public partial class BrushViewModel : RdvViewModel
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial HairPrestationDto? SelectedPrestation { get; set; }
|
public partial HairPrestationDto? SelectedPrestation { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
|
||||||
public partial ObservableCollection<SelectableHairPrestationItem> MultiPrestations { get; set; } = new();
|
|
||||||
|
|
||||||
|
|
||||||
public BrushViewModel(ActivityInfo activity, ActivityUserDisplayItem performer, CommandFormSummary form, BillingApiClient billingClient)
|
public BrushViewModel(ActivityInfo activity, ActivityUserDisplayItem performer, CommandFormSummary form, BillingApiClient billingClient)
|
||||||
: base(activity, performer, form, billingClient)
|
: base(activity, performer, form, billingClient)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async void ApplyExistingQuery(BillingQueryDetailsDto existingQuery)
|
public override async Task LoadAsync()
|
||||||
{
|
{
|
||||||
if (IsBrush && existingQuery.PrestationId is not null)
|
var prestations = await _billingClient.GetHairPrestationsAsync(Form.ActionName);
|
||||||
|
|
||||||
|
AvailablePrestations = new ObservableCollection<HairPrestationDto>
|
||||||
|
(prestations ?? new List<HairPrestationDto>());
|
||||||
|
|
||||||
|
if (SelectedPrestation is null)
|
||||||
{
|
{
|
||||||
SelectedPrestation = AvailablePrestations.FirstOrDefault(x => x.Id == existingQuery.PrestationId.Value);
|
SelectedPrestation = AvailablePrestations.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsMultiBrush)
|
}
|
||||||
|
|
||||||
|
protected override void ApplyExistingQuery(BillingQueryDetailsDto existingQuery)
|
||||||
|
{
|
||||||
|
base.ApplyExistingQuery(existingQuery);
|
||||||
|
|
||||||
|
if (existingQuery.PrestationId is not null)
|
||||||
{
|
{
|
||||||
var selectedIds = existingQuery.PrestationIds is null
|
SelectedPrestation = AvailablePrestations.FirstOrDefault(x => x.Id == existingQuery.PrestationId.Value);
|
||||||
? new HashSet<long>()
|
|
||||||
: new HashSet<long>(existingQuery.PrestationIds);
|
|
||||||
foreach (var item in MultiPrestations)
|
|
||||||
{
|
|
||||||
item.IsSelected = selectedIds.Contains(item.Id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var prestations = await _billingClient.GetHairPrestationsAsync
|
if (SelectedPrestation is null)
|
||||||
(Form.ActionName).ConfigureAwait(true);
|
{
|
||||||
AvailablePrestations = new ObservableCollection<HairPrestationDto>
|
SelectedPrestation = AvailablePrestations.FirstOrDefault();
|
||||||
(prestations ?? new List<HairPrestationDto>());
|
}
|
||||||
SelectedPrestation = AvailablePrestations.FirstOrDefault();
|
|
||||||
MultiPrestations = new ObservableCollection<SelectableHairPrestationItem>(AvailablePrestations.Select(SelectableHairPrestationItem.FromDto));
|
|
||||||
|
|
||||||
StatusMessage = AvailablePrestations.Count == 0
|
StatusMessage = AvailablePrestations.Count == 0
|
||||||
? "Aucune prestation coiffure disponible."
|
? "Aucune prestation coiffure disponible."
|
||||||
|
|
@ -77,8 +78,23 @@ public partial class BrushViewModel : RdvViewModel
|
||||||
|
|
||||||
protected override async Task SubmitAsync()
|
protected override async Task SubmitAsync()
|
||||||
{
|
{
|
||||||
|
if (!Consent)
|
||||||
|
{
|
||||||
|
StatusMessage = "Le consentement est requis pour poster la commande.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Address))
|
||||||
|
{
|
||||||
|
StatusMessage = "L'adresse du rendez-vous est requise.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SelectedPrestation is null)
|
||||||
|
{
|
||||||
|
StatusMessage = "Sélectionnez une prestation coiffure.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IsBusy = true;
|
IsBusy = true;
|
||||||
try
|
try
|
||||||
|
|
@ -105,64 +121,30 @@ public partial class BrushViewModel : RdvViewModel
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (IsBrush)
|
payload.PrestationId = SelectedPrestation.Id;
|
||||||
|
|
||||||
|
if (IsEditingExisting)
|
||||||
{
|
{
|
||||||
if (SelectedPrestation is null)
|
await _billingClient.UpdateAsync(Form.ActionName, ExistingQueryId!.Value, payload).ConfigureAwait(true);
|
||||||
{
|
|
||||||
StatusMessage = "Sélectionnez une prestation coiffure.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
payload.PrestationId = SelectedPrestation.Id;
|
|
||||||
|
|
||||||
if (IsEditingExisting)
|
|
||||||
{
|
|
||||||
await _billingClient.UpdateAsync(Form.ActionName, ExistingQueryId!.Value, payload).ConfigureAwait(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _billingClient.CreateAsync(Form.ActionName, new
|
|
||||||
{
|
|
||||||
ActivityCode = Activity.Code,
|
|
||||||
PerformerId = Performer.PerformerId,
|
|
||||||
Consent,
|
|
||||||
EventDate = (DateTime?)EventDate,
|
|
||||||
Location = locationPayload,
|
|
||||||
PrestationId = SelectedPrestation.Id,
|
|
||||||
AdditionalInfo = string.IsNullOrWhiteSpace(AdditionalInfo) ? null : AdditionalInfo.Trim(),
|
|
||||||
Status = payload.Status,
|
|
||||||
}).ConfigureAwait(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (IsMultiBrush)
|
else
|
||||||
{
|
{
|
||||||
var selectedPrestations = MultiPrestations.Where(x => x.IsSelected).ToList();
|
await _billingClient.CreateAsync(Form.ActionName, new
|
||||||
if (selectedPrestations.Count == 0)
|
|
||||||
{
|
{
|
||||||
StatusMessage = "Sélectionnez au moins une prestation coiffure.";
|
ActivityCode = Activity.Code,
|
||||||
return;
|
PerformerId = Performer.PerformerId,
|
||||||
}
|
Consent,
|
||||||
|
EventDate = (DateTime?)EventDate,
|
||||||
payload.PrestationIds = selectedPrestations.Select(x => x.Id).ToList();
|
Location = locationPayload,
|
||||||
|
PrestationId = SelectedPrestation.Id,
|
||||||
if (IsEditingExisting)
|
AdditionalInfo = string.IsNullOrWhiteSpace(AdditionalInfo) ? null : AdditionalInfo.Trim(),
|
||||||
{
|
Status = payload.Status,
|
||||||
await _billingClient.UpdateAsync(Form.ActionName, ExistingQueryId!.Value, payload).ConfigureAwait(true);
|
}).ConfigureAwait(true);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _billingClient.CreateAsync(Form.ActionName, new
|
|
||||||
{
|
|
||||||
ActivityCode = Activity.Code,
|
|
||||||
PerformerId = Performer.PerformerId,
|
|
||||||
Consent,
|
|
||||||
EventDate = EventDate,
|
|
||||||
Location = locationPayload,
|
|
||||||
Prestations = selectedPrestations.Select(x => new { PrestationId = x.Id }).ToList(),
|
|
||||||
Status = payload.Status,
|
|
||||||
}).ConfigureAwait(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StatusMessage = IsEditingExisting
|
||||||
|
? $"Commande #{ExistingQueryId} mise à jour sur {BillingRoute} pour {Performer.UserName}."
|
||||||
|
: $"Commande transmise sur {BillingRoute} pour {Performer.UserName}.";
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
|
when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
using PostIt.ViewModels;
|
|
||||||
|
|
||||||
namespace PostIt.ViewModels.Comands;
|
|
||||||
|
|
||||||
public class MBrushViewModel : BillingCommandPageViewModel
|
|
||||||
{
|
|
||||||
public MBrushViewModel(ActivityInfo activity, ActivityUserDisplayItem performer, CommandFormSummary form, BillingApiClient billingClient)
|
|
||||||
: base(activity, performer, form, billingClient)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ApplyExistingQuery(BillingQueryDetailsDto existingQuery)
|
|
||||||
{
|
|
||||||
throw new System.NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
130
src/PostIt/PostIt/ViewModels/Commands/MBrushViewModel.cs
Normal file
130
src/PostIt/PostIt/ViewModels/Commands/MBrushViewModel.cs
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using Yavsc.Abstract.Workflow;
|
||||||
|
using Yavsc.Api.Client;
|
||||||
|
using Yavsc.Models.Billing;
|
||||||
|
|
||||||
|
namespace PostIt.ViewModels.Commands;
|
||||||
|
|
||||||
|
public partial class MBrushViewModel : BrushViewModel
|
||||||
|
{
|
||||||
|
public override string SupportMessage => "Choisissez une ou plusieurs prestations coiffure puis postez la commande.";
|
||||||
|
|
||||||
|
[ObservableProperty]
|
||||||
|
public partial ObservableCollection<SelectableHairPrestationItem> MultiPrestations { get; set; } = new();
|
||||||
|
|
||||||
|
public MBrushViewModel(ActivityInfo activity, ActivityUserDisplayItem performer, CommandFormSummary form, BillingApiClient billingClient)
|
||||||
|
: base(activity, performer, form, billingClient)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task LoadAsync()
|
||||||
|
{
|
||||||
|
await base.LoadAsync().ConfigureAwait(true);
|
||||||
|
MultiPrestations = new ObservableCollection<SelectableHairPrestationItem>(
|
||||||
|
AvailablePrestations.Select(SelectableHairPrestationItem.FromDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ApplyExistingQuery(BillingQueryDetailsDto existingQuery)
|
||||||
|
{
|
||||||
|
base.ApplyExistingQuery(existingQuery);
|
||||||
|
|
||||||
|
var selectedIds = existingQuery.PrestationIds is null
|
||||||
|
? new HashSet<long>()
|
||||||
|
: new HashSet<long>(existingQuery.PrestationIds);
|
||||||
|
|
||||||
|
foreach (var item in MultiPrestations)
|
||||||
|
{
|
||||||
|
item.IsSelected = selectedIds.Contains(item.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async Task SubmitAsync()
|
||||||
|
{
|
||||||
|
if (!Consent)
|
||||||
|
{
|
||||||
|
StatusMessage = "Le consentement est requis pour poster la commande.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(Address))
|
||||||
|
{
|
||||||
|
StatusMessage = "L'adresse du rendez-vous est requise.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedPrestations = MultiPrestations.Where(x => x.IsSelected).ToList();
|
||||||
|
if (selectedPrestations.Count == 0)
|
||||||
|
{
|
||||||
|
StatusMessage = "Sélectionnez au moins une prestation coiffure.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IsBusy = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var address = Address.Trim();
|
||||||
|
var locationPayload = BuildLocationPayload(address, Latitude, Longitude);
|
||||||
|
|
||||||
|
var payload = new BillingQueryDetailsDto
|
||||||
|
{
|
||||||
|
Id = ExistingQueryId ?? 0,
|
||||||
|
BillingCode = Form.ActionName,
|
||||||
|
ActivityCode = Activity.Code,
|
||||||
|
PerformerId = Performer.PerformerId,
|
||||||
|
Consent = Consent,
|
||||||
|
EventDate = EventDate,
|
||||||
|
Status = CommandStatus,
|
||||||
|
Reason = Reason.Trim(),
|
||||||
|
Location = new BillingLocationDto
|
||||||
|
{
|
||||||
|
Address = address,
|
||||||
|
Latitude = Latitude,
|
||||||
|
Longitude = Longitude,
|
||||||
|
},
|
||||||
|
PrestationIds = selectedPrestations.Select(x => x.Id).ToList(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (IsEditingExisting)
|
||||||
|
{
|
||||||
|
await _billingClient.UpdateAsync(Form.ActionName, ExistingQueryId!.Value, payload).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _billingClient.CreateAsync(Form.ActionName, new
|
||||||
|
{
|
||||||
|
ActivityCode = Activity.Code,
|
||||||
|
PerformerId = Performer.PerformerId,
|
||||||
|
Consent,
|
||||||
|
EventDate = EventDate,
|
||||||
|
Location = locationPayload,
|
||||||
|
Prestations = selectedPrestations.Select(x => new { PrestationId = x.Id }).ToList(),
|
||||||
|
Status = payload.Status,
|
||||||
|
}).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusMessage = IsEditingExisting
|
||||||
|
? $"Commande #{ExistingQueryId} mise à jour sur {BillingRoute} pour {Performer.UserName}."
|
||||||
|
: $"Commande transmise sur {BillingRoute} pour {Performer.UserName}.";
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
when (ex.StatusCode is HttpStatusCode.Unauthorized or HttpStatusCode.Forbidden)
|
||||||
|
{
|
||||||
|
StatusMessage = "Accès refusé au billing (scope 'api'). Déconnectez puis reconnectez-vous.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusMessage = $"Erreur lors de l'envoi de la commande: {ex.Message}";
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
IsBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,14 +12,16 @@ namespace PostIt.ViewModels.Commands;
|
||||||
|
|
||||||
public partial class RdvViewModel : BillingCommandPageViewModel
|
public partial class RdvViewModel : BillingCommandPageViewModel
|
||||||
{
|
{
|
||||||
|
public override string SupportMessage => "Complétez les informations du rendez-vous puis postez la commande.";
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial string Address { get; set; } = string.Empty;
|
public partial string Address { get; set; } = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial double Latitude { get; set; }
|
public partial double? Latitude { get; set; }
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
public partial double Longitude { get; set; }
|
public partial double? Longitude { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[ObservableProperty]
|
[ObservableProperty]
|
||||||
|
|
@ -48,12 +50,10 @@ public partial class RdvViewModel : BillingCommandPageViewModel
|
||||||
if (existingQuery.Location is not null)
|
if (existingQuery.Location is not null)
|
||||||
{
|
{
|
||||||
Address = existingQuery.Location.Address ?? string.Empty;
|
Address = existingQuery.Location.Address ?? string.Empty;
|
||||||
Latitude = existingQuery.Location.Latitude ?? 0;
|
Latitude = existingQuery.Location.Latitude;
|
||||||
Longitude = existingQuery.Location.Longitude ?? 0;
|
Longitude = existingQuery.Location.Longitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
StatusMessage = $"Commande #{existingQuery.Id} chargée.";
|
StatusMessage = $"Commande #{existingQuery.Id} chargée.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,7 +135,7 @@ public partial class RdvViewModel : BillingCommandPageViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (IsRdv && string.IsNullOrWhiteSpace(Reason))
|
if (string.IsNullOrWhiteSpace(Reason))
|
||||||
{
|
{
|
||||||
StatusMessage = "Le motif du rendez-vous est requis.";
|
StatusMessage = "Le motif du rendez-vous est requis.";
|
||||||
return;
|
return;
|
||||||
|
|
@ -168,27 +168,23 @@ public partial class RdvViewModel : BillingCommandPageViewModel
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (IsRdv)
|
if (IsEditingExisting)
|
||||||
{
|
{
|
||||||
if (IsEditingExisting)
|
await _billingClient.UpdateAsync(Form.ActionName, ExistingQueryId!.Value, payload).ConfigureAwait(true);
|
||||||
{
|
|
||||||
await _billingClient.UpdateAsync(Form.ActionName, ExistingQueryId!.Value, payload).ConfigureAwait(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _billingClient.CreateAsync(Form.ActionName, new
|
|
||||||
{
|
|
||||||
ActivityCode = Activity.Code,
|
|
||||||
PerformerId = Performer.PerformerId,
|
|
||||||
Consent,
|
|
||||||
EventDate = EventDate,
|
|
||||||
Location = locationPayload,
|
|
||||||
Reason = payload.Reason,
|
|
||||||
Status = payload.Status,
|
|
||||||
}).ConfigureAwait(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
await _billingClient.CreateAsync(Form.ActionName, new
|
||||||
|
{
|
||||||
|
ActivityCode = Activity.Code,
|
||||||
|
PerformerId = Performer.PerformerId,
|
||||||
|
Consent,
|
||||||
|
EventDate = EventDate,
|
||||||
|
Location = locationPayload,
|
||||||
|
Reason = payload.Reason,
|
||||||
|
Status = payload.Status,
|
||||||
|
}).ConfigureAwait(true);
|
||||||
|
}
|
||||||
|
|
||||||
StatusMessage = IsEditingExisting
|
StatusMessage = IsEditingExisting
|
||||||
? $"Commande #{ExistingQueryId} mise à jour sur {BillingRoute} pour {Performer.UserName}."
|
? $"Commande #{ExistingQueryId} mise à jour sur {BillingRoute} pour {Performer.UserName}."
|
||||||
|
|
@ -207,4 +203,9 @@ public partial class RdvViewModel : BillingCommandPageViewModel
|
||||||
IsBusy = false;
|
IsBusy = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override Task LoadAsync()
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
10
src/PostIt/PostIt/ViewModels/RemoteViewModelBase.cs
Normal file
10
src/PostIt/PostIt/ViewModels/RemoteViewModelBase.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PostIt.ViewModels;
|
||||||
|
|
||||||
|
public abstract class RemoteViewModelBase : ViewModelBase
|
||||||
|
{
|
||||||
|
public abstract Task LoadAsync();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
|
||||||
namespace PostIt.ViewModels;
|
namespace PostIt.ViewModels;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,15 @@ namespace Yavsc.Models.Messaging
|
||||||
{
|
{
|
||||||
Topic = "Estimation";
|
Topic = "Estimation";
|
||||||
Estimation = estimate;
|
Estimation = estimate;
|
||||||
perfer = estimate.Owner;
|
performer = estimate.Owner;
|
||||||
|
|
||||||
ProviderInfo = new ProviderClientInfo {
|
ProviderInfo = new ProviderClientInfo {
|
||||||
Rate = perfer.Rate,
|
Rate = performer.Rate,
|
||||||
UserName = perfer.Performer.UserName,
|
UserName = performer.Performer.UserName,
|
||||||
Avatar = perfer.Performer.Avatar,
|
Avatar = performer.Performer.Avatar,
|
||||||
UserId = perfer.PerformerId
|
UserId = performer.PerformerId
|
||||||
};
|
};
|
||||||
Sender = perfer.Performer.UserName;
|
Sender = performer.Performer.UserName;
|
||||||
_localizer = SR;
|
_localizer = SR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Yavsc.Models.Messaging
|
||||||
ProviderClientInfo ProviderInfo { get; set; }
|
ProviderClientInfo ProviderInfo { get; set; }
|
||||||
Estimate Estimation { get; set; }
|
Estimate Estimation { get; set; }
|
||||||
|
|
||||||
private readonly PerformerProfile perfer;
|
private readonly PerformerProfile performer;
|
||||||
|
|
||||||
public string Topic
|
public string Topic
|
||||||
{
|
{
|
||||||
|
|
@ -47,7 +47,7 @@ namespace Yavsc.Models.Messaging
|
||||||
|
|
||||||
public string CreateBody()
|
public string CreateBody()
|
||||||
{
|
{
|
||||||
return string.Format(_localizer["EstimationMessageToClient"], perfer.Performer.UserName, this.Estimation.Bill.Addition());
|
return string.Format(_localizer["EstimationMessageToClient"], performer.Performer.UserName, this.Estimation.Bill.Addition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue