Identity reloaded
This commit is contained in:
parent
ef0f5ddcac
commit
333b066e66
33 changed files with 195 additions and 180 deletions
|
|
@ -10,7 +10,7 @@ namespace PostIt.Tests;
|
|||
/// URL emitted by OidcClient, extracts its <c>state</c>, and returns a
|
||||
/// BrowserResult that mimics the OIDC redirect-with-code callback.
|
||||
///
|
||||
/// The paired <see cref="OidcStubAuthority"/>'s token endpoint accepts
|
||||
/// The paired <see cref="OIDCStubAuthority"/>'s token endpoint accepts
|
||||
/// any authorization code, so we don't need to mint a real one here.
|
||||
/// </summary>
|
||||
public sealed class FakeAuthorizingBrowser
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public class LoginPageViewModelTests
|
|||
// short-circuits the system browser. The authority signs its
|
||||
// access_token with RS256; the fake browser captures the redirect
|
||||
// URI so the authority can complete the token exchange.
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
var browser = new FakeAuthorizingBrowser(authority.LoopbackRedirectUri);
|
||||
|
||||
var settings = new PostIt.Settings
|
||||
|
|
@ -96,7 +96,7 @@ public class LoginPageViewModelTests
|
|||
// double slash before /.well-known/openid-configuration. The
|
||||
// stub advertises itself without the trailing slash; OidcClient
|
||||
// must bridge.
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
var browser = new FakeAuthorizingBrowser(authority.LoopbackRedirectUri);
|
||||
|
||||
var settings = new PostIt.Settings
|
||||
|
|
@ -254,4 +254,4 @@ public class LoginPageViewModelTests
|
|||
"https://yavsc.example.com/.well-known/openid-configuration",
|
||||
vm.StatusMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace PostIt.Tests;
|
|||
/// the browser intercepts the authorize redirect, the server completes
|
||||
/// the token exchange.
|
||||
/// </summary>
|
||||
public sealed class OidcStubAuthority : IAsyncDisposable, IDisposable
|
||||
public sealed class OIDCStubAuthority : IAsyncDisposable, IDisposable
|
||||
{
|
||||
private readonly HttpListener _listener;
|
||||
private readonly RSA _rsa;
|
||||
|
|
@ -30,7 +30,7 @@ public sealed class OidcStubAuthority : IAsyncDisposable, IDisposable
|
|||
public string Issuer { get; }
|
||||
public string LoopbackRedirectUri { get; }
|
||||
|
||||
private OidcStubAuthority(HttpListener listener, RSA rsa, string kid, string issuer, string loopback)
|
||||
private OIDCStubAuthority(HttpListener listener, RSA rsa, string kid, string issuer, string loopback)
|
||||
{
|
||||
_listener = listener;
|
||||
_rsa = rsa;
|
||||
|
|
@ -39,7 +39,7 @@ public sealed class OidcStubAuthority : IAsyncDisposable, IDisposable
|
|||
LoopbackRedirectUri = loopback;
|
||||
}
|
||||
|
||||
public static async Task<OidcStubAuthority> StartAsync()
|
||||
public static async Task<OIDCStubAuthority> StartAsync()
|
||||
{
|
||||
// Pick a free loopback port.
|
||||
var port = GetFreePort();
|
||||
|
|
@ -53,7 +53,7 @@ public sealed class OidcStubAuthority : IAsyncDisposable, IDisposable
|
|||
var rsa = RSA.Create(2048);
|
||||
var kid = "test-key-1";
|
||||
|
||||
var authority = new OidcStubAuthority(listener, rsa, kid, prefix.TrimEnd('/'), loopback);
|
||||
var authority = new OIDCStubAuthority(listener, rsa, kid, prefix.TrimEnd('/'), loopback);
|
||||
_ = Task.Run(() => authority.AcceptLoopAsync(authority._cts.Token));
|
||||
return authority;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace PostIt.Tests;
|
|||
/// End-to-end coverage of <see cref="YavscApiClient"/>: silent
|
||||
/// refresh on a near-expiry access token, 401-driven refresh + retry,
|
||||
/// and persistence of the token bundle via <see cref="TokenStore"/>.
|
||||
/// Uses the project's <see cref="OidcStubAuthority"/> for the IdP and
|
||||
/// Uses the project's <see cref="OIDCStubAuthority"/> for the IdP and
|
||||
/// a tiny in-process HTTP listener for the API server side.
|
||||
/// </summary>
|
||||
public class YavscApiClientTests
|
||||
|
|
@ -45,7 +45,7 @@ public class YavscApiClientTests
|
|||
// in-memory access token as expired and re-run a call. The
|
||||
// refresh path must rotate the refresh token transparently
|
||||
// and the API call must succeed with the new token.
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer();
|
||||
await apiServer.StartAsync();
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ public class YavscApiClientTests
|
|||
var reloaded = new YavscApiClient(settings, new TokenStore(tokensPath));
|
||||
|
||||
var posts = await reloaded.CallAsync<List<StubApiServer.Post>>(
|
||||
HttpMethod.Get, "posts");
|
||||
HttpMethod.Get, "posts", TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(posts);
|
||||
Assert.NotEmpty(posts);
|
||||
|
|
@ -85,7 +85,7 @@ public class YavscApiClientTests
|
|||
{
|
||||
// API server returns 401 on the first request, 200 on the next.
|
||||
// YavscApiClient must refresh, then retry exactly once.
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer(forceFirstRequest: true);
|
||||
await apiServer.StartAsync();
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ public class YavscApiClientTests
|
|||
settings, authority, tokensPath);
|
||||
|
||||
var posts = await client.CallAsync<List<StubApiServer.Post>>(
|
||||
HttpMethod.Get, "posts");
|
||||
HttpMethod.Get, "posts", TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotEmpty(posts);
|
||||
Assert.Equal(2, apiServer.RequestCount);
|
||||
|
|
@ -125,14 +125,15 @@ public class YavscApiClientTests
|
|||
var client = new YavscApiClient(settings, new TokenStore(Path.Combine(
|
||||
Path.GetTempPath(), $"postit-tests-noop-{Guid.NewGuid():N}.json")));
|
||||
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
client.CallAsync<JsonElement>(HttpMethod.Get, "posts"));
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() =>
|
||||
client.CallAsync<JsonElement>(HttpMethod.Get, "posts", TestContext.Current.CancellationToken));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HasValidSession_is_true_after_login()
|
||||
{
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer();
|
||||
await apiServer.StartAsync();
|
||||
|
||||
|
|
@ -154,7 +155,7 @@ public class YavscApiClientTests
|
|||
|
||||
// --- helpers --------------------------------------------------------
|
||||
|
||||
private static PostIt.Settings BuildSettings(OidcStubAuthority authority, string apiBaseUrl) => new()
|
||||
private static PostIt.Settings BuildSettings(OIDCStubAuthority authority, string apiBaseUrl) => new()
|
||||
{
|
||||
Authentication = new AuthenticationSettings
|
||||
{
|
||||
|
|
@ -167,7 +168,7 @@ public class YavscApiClientTests
|
|||
};
|
||||
|
||||
private static async Task<YavscApiClient> LoginAndPersistAsync(
|
||||
PostIt.Settings settings, OidcStubAuthority authority, string tokensPath)
|
||||
PostIt.Settings settings, OIDCStubAuthority authority, string tokensPath)
|
||||
{
|
||||
var browser = new FakeAuthorizingBrowser(authority.LoopbackRedirectUri);
|
||||
var client = new YavscApiClient(settings, new TokenStore(tokensPath));
|
||||
|
|
@ -181,7 +182,7 @@ public class YavscApiClientTests
|
|||
/// <summary>
|
||||
/// YavscApiClient.LoginInteractiveAsync delegates to
|
||||
/// Platform.CreateBrowser. We can't override that static cleanly
|
||||
/// from xunit.v3, so we rebuild the call by re-routing the
|
||||
/// from XUnit.v3, so we rebuild the call by re-routing the
|
||||
/// Platform.CreateBrowser delegate for the duration of the call.
|
||||
/// </summary>
|
||||
private static async Task LoginWithBrowserAsync(
|
||||
|
|
@ -214,7 +215,7 @@ public class YavscApiClientTests
|
|||
File.WriteAllText(tokensPath, JsonSerializer.Serialize(record));
|
||||
}
|
||||
|
||||
// --- OidcLoginPhase progress tests ---------------------------------
|
||||
// --- OIDCLoginPhase progress tests ---------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Collecting Progress<T> is documented to capture reports
|
||||
|
|
@ -225,7 +226,7 @@ public class YavscApiClientTests
|
|||
[Fact]
|
||||
public async Task LoginInteractiveAsync_reports_Discovering_then_Success()
|
||||
{
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer();
|
||||
await apiServer.StartAsync();
|
||||
|
||||
|
|
@ -234,18 +235,18 @@ public class YavscApiClientTests
|
|||
var client = new YavscApiClient(settings, new TokenStore(tokensPath));
|
||||
var browser = new FakeAuthorizingBrowser(authority.LoopbackRedirectUri);
|
||||
|
||||
var reported = new System.Collections.Generic.List<OidcLoginPhase>();
|
||||
var progress = new SyncProgress<OidcLoginPhase>(reported);
|
||||
var reported = new System.Collections.Generic.List<OIDCLoginPhase>();
|
||||
var progress = new SyncProgress<OIDCLoginPhase>(reported);
|
||||
|
||||
try
|
||||
{
|
||||
await LoginWithBrowserAsync(client, browser.CreateBrowser(), progress);
|
||||
// SyncProgress captures reports synchronously — no flush needed.
|
||||
|
||||
Assert.Contains(OidcLoginPhase.Discovering, reported);
|
||||
Assert.Contains(OidcLoginPhase.OpeningBrowser, reported);
|
||||
Assert.Contains(OidcLoginPhase.ExchangingCode, reported);
|
||||
Assert.Equal(OidcLoginPhase.Success, Last(reported));
|
||||
Assert.Contains(OIDCLoginPhase.Discovering, reported);
|
||||
Assert.Contains(OIDCLoginPhase.OpeningBrowser, reported);
|
||||
Assert.Contains(OIDCLoginPhase.ExchangingCode, reported);
|
||||
Assert.Equal(OIDCLoginPhase.Success, Last(reported));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -256,24 +257,24 @@ public class YavscApiClientTests
|
|||
[Fact]
|
||||
public async Task LoginInteractiveAsync_reports_Error_when_browser_missing()
|
||||
{
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer();
|
||||
await apiServer.StartAsync();
|
||||
|
||||
var settings = BuildSettings(authority, apiServer.BaseUrl);
|
||||
var client = new YavscApiClient(settings, new TokenStore(TokensPath()));
|
||||
var reported = new System.Collections.Generic.List<OidcLoginPhase>();
|
||||
var progress = new SyncProgress<OidcLoginPhase>(reported);
|
||||
var reported = new System.Collections.Generic.List<OIDCLoginPhase>();
|
||||
var progress = new SyncProgress<OIDCLoginPhase>(reported);
|
||||
|
||||
var original = Platform.CreateBrowser;
|
||||
try
|
||||
{
|
||||
Platform.CreateBrowser = () => null; // simulate no browser wired up
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => client.LoginInteractiveAsync(progress));
|
||||
() => client.LoginInteractiveAsync(progress, TestContext.Current.CancellationToken));
|
||||
// SyncProgress captures reports synchronously — no flush needed.
|
||||
|
||||
Assert.Equal(OidcLoginPhase.Error, Last(reported));
|
||||
Assert.Equal(OIDCLoginPhase.Error, Last(reported));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -284,7 +285,7 @@ public class YavscApiClientTests
|
|||
[Fact]
|
||||
public async Task TrySilentLoginAsync_returns_false_when_no_bundle_on_disk()
|
||||
{
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer();
|
||||
await apiServer.StartAsync();
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ public class YavscApiClientTests
|
|||
// Tokens file deliberately doesn't exist.
|
||||
var client = new YavscApiClient(settings, new TokenStore(tokensPath));
|
||||
|
||||
var ok = await client.TrySilentLoginAsync();
|
||||
var ok = await client.TrySilentLoginAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.False(ok);
|
||||
Assert.False(client.HasValidSession);
|
||||
}
|
||||
|
|
@ -301,7 +302,7 @@ public class YavscApiClientTests
|
|||
[Fact]
|
||||
public async Task TrySilentLoginAsync_returns_true_when_access_token_still_valid()
|
||||
{
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer();
|
||||
await apiServer.StartAsync();
|
||||
|
||||
|
|
@ -314,7 +315,7 @@ public class YavscApiClientTests
|
|||
{
|
||||
await LoginWithBrowserAsync(client, browser.CreateBrowser());
|
||||
// Login fresh → access token is far from expiry.
|
||||
var ok = await client.TrySilentLoginAsync();
|
||||
var ok = await client.TrySilentLoginAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.True(ok);
|
||||
Assert.True(client.HasValidSession);
|
||||
}
|
||||
|
|
@ -327,7 +328,7 @@ public class YavscApiClientTests
|
|||
[Fact]
|
||||
public async Task TrySilentLoginAsync_returns_true_when_refresh_succeeds()
|
||||
{
|
||||
using var authority = await OidcStubAuthority.StartAsync();
|
||||
using var authority = await OIDCStubAuthority.StartAsync();
|
||||
using var apiServer = new StubApiServer();
|
||||
await apiServer.StartAsync();
|
||||
|
||||
|
|
@ -351,13 +352,13 @@ public class YavscApiClientTests
|
|||
// matches the disk: access expired, refresh still good.
|
||||
var client = new YavscApiClient(settings, store);
|
||||
|
||||
var reported = new System.Collections.Generic.List<OidcLoginPhase>();
|
||||
var progress = new SyncProgress<OidcLoginPhase>(reported);
|
||||
var reported = new System.Collections.Generic.List<OIDCLoginPhase>();
|
||||
var progress = new SyncProgress<OIDCLoginPhase>(reported);
|
||||
|
||||
var ok = await client.TrySilentLoginAsync(progress);
|
||||
var ok = await client.TrySilentLoginAsync(progress, TestContext.Current.CancellationToken);
|
||||
Assert.True(ok, "silent refresh should succeed via the stub authority.");
|
||||
Assert.Contains(OidcLoginPhase.ExchangingCode, reported);
|
||||
Assert.Equal(OidcLoginPhase.Success, Last(reported));
|
||||
Assert.Contains(OIDCLoginPhase.ExchangingCode, reported);
|
||||
Assert.Equal(OIDCLoginPhase.Success, Last(reported));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -430,7 +431,7 @@ public class YavscApiClientTests
|
|||
/// overload stays for tests that don't care about phase events.
|
||||
/// </summary>
|
||||
private static async Task LoginWithBrowserAsync(
|
||||
YavscApiClient client, IBrowser browser, IProgress<OidcLoginPhase>? progress = null)
|
||||
YavscApiClient client, IBrowser browser, IProgress<OIDCLoginPhase>? progress = null)
|
||||
{
|
||||
var original = Platform.CreateBrowser;
|
||||
try
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace PostIt.Services;
|
|||
/// The set is deliberately small: each value is a milestone an
|
||||
/// operator can grep for in logs / StatusMessage, not a heartbeat.
|
||||
/// </summary>
|
||||
public enum OidcLoginPhase
|
||||
public enum OIDCLoginPhase
|
||||
{
|
||||
/// <summary>No login in flight (or login has settled).</summary>
|
||||
Idle,
|
||||
|
|
|
|||
|
|
@ -91,15 +91,15 @@ public class YavscApiClient : IAsyncDisposable
|
|||
/// <see cref="LoginPageViewModel.StatusMessage"/> for the human
|
||||
/// text (URLs, error detail).</param>
|
||||
public async Task LoginInteractiveAsync(
|
||||
IProgress<OidcLoginPhase>? progress = null,
|
||||
IProgress<OIDCLoginPhase>? progress = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.Discovering);
|
||||
progress?.Report(OIDCLoginPhase.Discovering);
|
||||
|
||||
var browser = Platform.CreateBrowser?.Invoke();
|
||||
if (browser is null)
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.Error);
|
||||
progress?.Report(OIDCLoginPhase.Error);
|
||||
throw new InvalidOperationException("No browser is available on this platform.");
|
||||
}
|
||||
|
||||
|
|
@ -114,20 +114,20 @@ public class YavscApiClient : IAsyncDisposable
|
|||
// the moment we ask the browser to open (covers the entire
|
||||
// user-driven window including the AwaitingCallback wait), and
|
||||
// the moment we trade the code for tokens.
|
||||
progress?.Report(OidcLoginPhase.OpeningBrowser);
|
||||
progress?.Report(OIDCLoginPhase.OpeningBrowser);
|
||||
var result = await client.LoginAsync(new LoginRequest(), ct).ConfigureAwait(false);
|
||||
|
||||
if (result.IsError)
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.Error);
|
||||
progress?.Report(OIDCLoginPhase.Error);
|
||||
throw new InvalidOperationException($"OIDC login failed: {result.Error}");
|
||||
}
|
||||
|
||||
progress?.Report(OidcLoginPhase.ExchangingCode);
|
||||
progress?.Report(OIDCLoginPhase.ExchangingCode);
|
||||
|
||||
if (string.IsNullOrEmpty(result.RefreshToken))
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.Error);
|
||||
progress?.Report(OIDCLoginPhase.Error);
|
||||
throw new InvalidOperationException(
|
||||
"Missing refresh_token — vérifie le scope 'offline_access'.");
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ public class YavscApiClient : IAsyncDisposable
|
|||
IdToken: result.IdentityToken);
|
||||
|
||||
_store.Save(_tokens);
|
||||
progress?.Report(OidcLoginPhase.Success);
|
||||
progress?.Report(OIDCLoginPhase.Success);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -152,7 +152,7 @@ public class YavscApiClient : IAsyncDisposable
|
|||
/// phase and returns false so the UI can keep going.
|
||||
/// </summary>
|
||||
public async Task<bool> TrySilentLoginAsync(
|
||||
IProgress<OidcLoginPhase>? progress = null,
|
||||
IProgress<OIDCLoginPhase>? progress = null,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
if (!HasValidSession) return false;
|
||||
|
|
@ -161,7 +161,7 @@ public class YavscApiClient : IAsyncDisposable
|
|||
// Access token still has plenty of life — nothing to do.
|
||||
if (_tokens.AccessTokenExpiresAt - DateTimeOffset.UtcNow > RefreshSkew)
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.Success);
|
||||
progress?.Report(OIDCLoginPhase.Success);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -172,19 +172,19 @@ public class YavscApiClient : IAsyncDisposable
|
|||
// the user back to the login page.
|
||||
try
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.ExchangingCode);
|
||||
progress?.Report(OIDCLoginPhase.ExchangingCode);
|
||||
await ForceRefreshAsync(ct).ConfigureAwait(false);
|
||||
progress?.Report(OidcLoginPhase.Success);
|
||||
progress?.Report(OIDCLoginPhase.Success);
|
||||
return true;
|
||||
}
|
||||
catch (RefreshFailedException)
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.Idle);
|
||||
progress?.Report(OIDCLoginPhase.Idle);
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
progress?.Report(OidcLoginPhase.Idle);
|
||||
progress?.Report(OIDCLoginPhase.Idle);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
/// callback hand-off: when AwaitingCallback never resolves,
|
||||
/// the OS never re-launched PostIt with the postit:// URL.
|
||||
/// </summary>
|
||||
private OidcLoginPhase _phase = OidcLoginPhase.Idle;
|
||||
public OidcLoginPhase Phase
|
||||
private OIDCLoginPhase _phase = OIDCLoginPhase.Idle;
|
||||
public OIDCLoginPhase Phase
|
||||
{
|
||||
get => _phase;
|
||||
private set
|
||||
|
|
@ -122,13 +122,13 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
/// </summary>
|
||||
public string PhaseLabel => _phase switch
|
||||
{
|
||||
OidcLoginPhase.Idle => "En attente",
|
||||
OidcLoginPhase.Discovering => "Découverte OIDC…",
|
||||
OidcLoginPhase.OpeningBrowser => "Ouverture du navigateur…",
|
||||
OidcLoginPhase.AwaitingCallback => "En attente du callback postit://…",
|
||||
OidcLoginPhase.ExchangingCode => "Échange du code contre les jetons…",
|
||||
OidcLoginPhase.Success => "Connecté",
|
||||
OidcLoginPhase.Error => "Erreur",
|
||||
OIDCLoginPhase.Idle => "En attente",
|
||||
OIDCLoginPhase.Discovering => "Découverte OIDC…",
|
||||
OIDCLoginPhase.OpeningBrowser => "Ouverture du navigateur…",
|
||||
OIDCLoginPhase.AwaitingCallback => "En attente du callback postit://…",
|
||||
OIDCLoginPhase.ExchangingCode => "Échange du code contre les jetons…",
|
||||
OIDCLoginPhase.Success => "Connecté",
|
||||
OIDCLoginPhase.Error => "Erreur",
|
||||
_ => _phase.ToString(),
|
||||
};
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
// The progress sink drives Phase / PhaseLabel; StatusMessage
|
||||
// keeps the text detail (URLs, error messages). Same
|
||||
// underlying flow, two views.
|
||||
var progress = new Progress<OidcLoginPhase>(p => Phase = p);
|
||||
var progress = new Progress<OIDCLoginPhase>(p => Phase = p);
|
||||
await LoginInteractiveCoreAsync(_api, progress);
|
||||
|
||||
IsBusy = false;
|
||||
|
|
@ -299,7 +299,7 @@ public partial class LoginPageViewModel : ViewModelBase
|
|||
/// </summary>
|
||||
private async Task LoginInteractiveCoreAsync(
|
||||
YavscApiClient api,
|
||||
IProgress<OidcLoginPhase>? progress = null)
|
||||
IProgress<OIDCLoginPhase>? progress = null)
|
||||
{
|
||||
var original = Platform.CreateBrowser;
|
||||
try
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace Yavsc.Controllers
|
|||
{
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var now = DateTime.Now;
|
||||
|
||||
|
||||
var result = _context.RdvQueries.Include(c => c.Location).
|
||||
Include(c => c.Client).Where(c => c.PerformerId == uid && c.Id < maxId && c.EventDate > now
|
||||
&& c.ValidationDate == null).
|
||||
|
|
@ -49,12 +49,12 @@ namespace Yavsc.Controllers
|
|||
{
|
||||
Client = new ClientProviderInfo {
|
||||
UserName = c.Client.UserName,
|
||||
UserId = c.ClientId,
|
||||
UserId = c.ClientId,
|
||||
Avatar = c.Client.Avatar },
|
||||
Location = c.Location,
|
||||
EventDate = c.EventDate,
|
||||
Id = c.Id,
|
||||
Previsional = c.Previsional,
|
||||
Previsional = c.Provisional,
|
||||
Reason = c.Reason,
|
||||
ActivityCode = c.ActivityCode,
|
||||
BillingCode = BillingCodes.Rdv
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace Yavsc.ApiControllers
|
|||
// user, as a client
|
||||
public IActionResult Index()
|
||||
{
|
||||
|
||||
|
||||
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
|
||||
var now = DateTime.Now;
|
||||
|
|
@ -151,7 +151,7 @@ namespace Yavsc.ApiControllers
|
|||
{
|
||||
|
||||
HairCutQuery query = await _context.HairCutQueries.Include(q => q.Client).
|
||||
Include(q => q.Client.PostalAddress).Include(q => q.Prestation).Include(q=>q.Regularisation)
|
||||
Include(q => q.Client.PostalAddress).Include(q => q.Prestation).Include(q=>q.Regularization)
|
||||
.SingleAsync(q => q.Id == id);
|
||||
if (query.PaymentId!=null)
|
||||
return new BadRequestObjectResult(new { error = "An existing payment process already exists" });
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace Yavsc
|
|||
WorkflowHelpers.ConfigureBillingService();
|
||||
|
||||
var firstRegistrar = new Func<ApplicationDbContext, long, IQuery>((db, id) =>
|
||||
db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id));
|
||||
db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularization).Single(q => q.Id == id));
|
||||
|
||||
const string testCode = "Brush";
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Yavsc.Controllers
|
|||
// GET: GeneralSettings
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
return View(await _context.GeneralSettings.ToListAsync());
|
||||
return View(await _context.MusicLoverSettings.ToListAsync());
|
||||
}
|
||||
|
||||
// GET: GeneralSettings/Details/5
|
||||
|
|
@ -28,7 +28,7 @@ namespace Yavsc.Controllers
|
|||
return NotFound();
|
||||
}
|
||||
|
||||
MusicLoverSettings generalSettings = await _context.GeneralSettings.SingleAsync(m => m.UserId == id);
|
||||
MusicLoverSettings generalSettings = await _context.MusicLoverSettings.SingleAsync(m => m.UserId == id);
|
||||
if (generalSettings == null)
|
||||
{
|
||||
return NotFound();
|
||||
|
|
@ -50,7 +50,7 @@ namespace Yavsc.Controllers
|
|||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
_context.GeneralSettings.Add(generalSettings);
|
||||
_context.MusicLoverSettings.Add(generalSettings);
|
||||
await _context.SaveChangesAsync();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ namespace Yavsc.Controllers
|
|||
return NotFound();
|
||||
}
|
||||
|
||||
MusicLoverSettings generalSettings = await _context.GeneralSettings.SingleAsync(m => m.UserId == id);
|
||||
MusicLoverSettings generalSettings = await _context.MusicLoverSettings.SingleAsync(m => m.UserId == id);
|
||||
if (generalSettings == null)
|
||||
{
|
||||
return NotFound();
|
||||
|
|
@ -96,7 +96,7 @@ namespace Yavsc.Controllers
|
|||
return NotFound();
|
||||
}
|
||||
|
||||
MusicLoverSettings generalSettings = await _context.GeneralSettings.SingleAsync(m => m.UserId == id);
|
||||
MusicLoverSettings generalSettings = await _context.MusicLoverSettings.SingleAsync(m => m.UserId == id);
|
||||
if (generalSettings == null)
|
||||
{
|
||||
return NotFound();
|
||||
|
|
@ -110,8 +110,8 @@ namespace Yavsc.Controllers
|
|||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteConfirmed(string id)
|
||||
{
|
||||
MusicLoverSettings generalSettings = await _context.GeneralSettings.SingleAsync(m => m.UserId == id);
|
||||
_context.GeneralSettings.Remove(generalSettings);
|
||||
MusicLoverSettings generalSettings = await _context.MusicLoverSettings.SingleAsync(m => m.UserId == id);
|
||||
_context.MusicLoverSettings.Remove(generalSettings);
|
||||
await _context.SaveChangesAsync();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace Yavsc.Controllers
|
|||
this.haircutLocalizer = haircutLocalizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private async Task<HairCutQuery> GetQuery(long id)
|
||||
{
|
||||
var query = await _context.HairCutQueries
|
||||
|
|
@ -58,7 +58,7 @@ namespace Yavsc.Controllers
|
|||
.Include(x => x.Prestation)
|
||||
.Include(x => x.PerformerProfile.Performer)
|
||||
.Include(x => x.PerformerProfile.Performer.DeviceDeclaration)
|
||||
.Include(x => x.Regularisation)
|
||||
.Include(x => x.Regularization)
|
||||
.SingleAsync(m => m.Id == id);
|
||||
query.SelectedProfile = await _context.BrusherProfile.SingleAsync(b => b.UserId == query.PerformerId);
|
||||
return query;
|
||||
|
|
@ -82,11 +82,11 @@ namespace Yavsc.Controllers
|
|||
}
|
||||
var paymentInfo = await _context.ConfirmPayment(User.GetUserId(), PayerID, token);
|
||||
ViewBag.paymentinfo = paymentInfo;
|
||||
command.Regularisation = paymentInfo.DbContent;
|
||||
command.Regularization = paymentInfo.DbContent;
|
||||
command.PaymentId = token;
|
||||
bool paymentOk = false;
|
||||
if (paymentInfo.DetailsFromPayPal != null)
|
||||
if (paymentInfo.DetailsFromPayPal.Ack == AckCodeType.SUCCESS)
|
||||
if (paymentInfo.DetailsFromPayPal.Ack == AckCodeType.SUCCESS)
|
||||
{
|
||||
// FIXME Assert (command.ValidationDate == null)
|
||||
if (command.ValidationDate == null) {
|
||||
|
|
@ -174,7 +174,7 @@ namespace Yavsc.Controllers
|
|||
.Include(x => x.PerformerProfile)
|
||||
.Include(x => x.Prestation)
|
||||
.Include(x => x.PerformerProfile.Performer)
|
||||
.Include(x => x.Regularisation)
|
||||
.Include(x => x.Regularization)
|
||||
.SingleOrDefaultAsync(m => m.Id == id);
|
||||
if (command == null)
|
||||
{
|
||||
|
|
@ -224,7 +224,7 @@ namespace Yavsc.Controllers
|
|||
.FirstOrDefault(
|
||||
x => x.PerformerId == model.PerformerId
|
||||
);
|
||||
|
||||
|
||||
|
||||
if (taintIds != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Yavsc.Controllers
|
|||
private readonly ApplicationDbContext _context;
|
||||
readonly IStringLocalizer<ProjectController> _localizer;
|
||||
readonly IStringLocalizer<BugController> _bugLocalizer;
|
||||
|
||||
|
||||
public ProjectController(ApplicationDbContext context,
|
||||
IStringLocalizer<ProjectController> localizer,
|
||||
IStringLocalizer<BugController> bugLocalizer
|
||||
|
|
@ -32,7 +32,7 @@ namespace Yavsc.Controllers
|
|||
// GET: Project
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var applicationDbContext = _context.Project.Include(p => p.Client).Include(p => p.Context).Include(p => p.PerformerProfile).Include(p => p.Regularisation).Include(p => p.Repository);
|
||||
var applicationDbContext = _context.Project.Include(p => p.Client).Include(p => p.Context).Include(p => p.PerformerProfile).Include(p => p.Regularization).Include(p => p.Repository);
|
||||
return View(await applicationDbContext.ToListAsync());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
|
||||
<!-- Yavsc.Org-specific versions -->
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="AsciiDocSharp" Version="0.2.0" />
|
||||
<PackageVersion Include="AsciiDocSharp.Converters.Html" Version="0.2.0" />
|
||||
<PackageVersion Include="AsciiDocSharp" Version="0.1.0" />
|
||||
<PackageVersion Include="AsciiDocSharp.Converters.Html" Version="0.1.0" />
|
||||
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
|
||||
<PackageVersion Include="Google.Apis.Compute.v1" Version="1.74.0.4138" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.EntityFramework.Storage" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.Security" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.Storage" Version="8.0.5-preview-net9" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.AspNetIdentity" Version="8.1.0-alpha.171" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.EntityFramework.Storage" Version="8.1.0-alpha.171" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.Security" Version="8.1.0-alpha.171" />
|
||||
<PackageVersion Include="HigginsSoft.IdentityServer8.Storage" Version="8.1.0-alpha.171" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Antiforgery" Version="2.3.11" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Google" Version="10.0.9" />
|
||||
<PackageVersion Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.9" />
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ namespace Yavsc.Helpers
|
|||
{
|
||||
Sender = query.ClientId,
|
||||
Reason = query.Reason,
|
||||
Client = new ClientProviderInfo {
|
||||
Client = new ClientProviderInfo {
|
||||
UserName = query.Client.UserName ,
|
||||
UserId = query.ClientId,
|
||||
Avatar = query.Client.Avatar } ,
|
||||
Previsional = query.Previsional,
|
||||
Previsional = query.Provisional,
|
||||
EventDate = query.EventDate,
|
||||
Location = query.Location,
|
||||
Id = query.Id,
|
||||
|
|
@ -44,7 +44,7 @@ namespace Yavsc.Helpers
|
|||
var yaev = query.CreateEvent("NewHairCutQuery",
|
||||
string.Format(SR["HairCutQueryValidation"],query.Client.UserName),
|
||||
$"{query.Client.Id}");
|
||||
|
||||
|
||||
|
||||
return yaev;
|
||||
}
|
||||
|
|
@ -58,12 +58,12 @@ namespace Yavsc.Helpers
|
|||
var yaev = new HairCutQueryEvent("newCommand")
|
||||
{
|
||||
Sender = query.ClientId,
|
||||
|
||||
Client = new ClientProviderInfo {
|
||||
|
||||
Client = new ClientProviderInfo {
|
||||
UserName = query.Client.UserName ,
|
||||
UserId = query.ClientId,
|
||||
Avatar = query.Client.Avatar } ,
|
||||
Previsional = query.Previsional,
|
||||
Previsional = query.Provisional,
|
||||
EventDate = query.EventDate,
|
||||
Location = query.Location,
|
||||
Id = query.Id,
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
<dt> @Html.DisplayNameFor(m => m.Location)
|
||||
</dt>
|
||||
<dd>@if (Model.Location == null) {
|
||||
<p>Pas de lieu convenu ...</p>
|
||||
<p>Pas de lieu convenu ...</p>
|
||||
} else {
|
||||
<label for="Location">Location</label>
|
||||
@Html.DisplayFor(m => m.Location)
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
</dd>
|
||||
<dt>Notification
|
||||
</dt>
|
||||
<dd>@if (ViewBag.GooglePayload !=null)
|
||||
<dd>@if (ViewBag.GooglePayload !=null)
|
||||
{
|
||||
@if (ViewBag.GooglePayload.success>0) {
|
||||
<h4>GCM Notifications sent</h4>
|
||||
|
|
@ -85,10 +85,10 @@
|
|||
</dt>
|
||||
<dd>@await Component.InvokeAsync("Bill", Model)
|
||||
</dd>
|
||||
|
||||
<dt>@Html.DisplayNameFor(m=>m.Regularisation)</dt>
|
||||
|
||||
<dt>@Html.DisplayNameFor(m=>m.Regularization)</dt>
|
||||
<dd> @await Component.InvokeAsync("PayPalButton", Model)
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
<div class="form-group">
|
||||
<label asp-for="ActivityCode" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<select asp-for="ActivityCode" class ="form-control" asp-items="@ViewBag.ActivityCodeItems" ></select>
|
||||
<select asp-for="ActivityCode" class ="form-control" asp-items="@ViewBag.ActivityCodeItems" ></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ClientId" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
|
|
@ -57,12 +57,12 @@
|
|||
<div class="col-md-10">
|
||||
<select asp-for="PerformerId" class ="form-control" asp-items="@ViewBag.PerformerIdItems"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Previsional" class="col-md-2 control-label"></label>
|
||||
<label asp-for="Provisional" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Previsional" class="form-control" />
|
||||
<span asp-validation-for="Previsional" class="text-danger" ></span>
|
||||
<input asp-for="Provisional" class="form-control" />
|
||||
<span asp-validation-for="Provisional" class="text-danger" ></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@
|
|||
@Html.DisplayFor(model => model.OwnerId)
|
||||
</dd>
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.Previsional)
|
||||
@Html.DisplayNameFor(model => model.Provisional)
|
||||
</dt>
|
||||
<dd>
|
||||
@Html.DisplayFor(model => model.Previsional)
|
||||
@Html.DisplayFor(model => model.Provisional)
|
||||
</dd>
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.DateModified)
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@
|
|||
@Html.DisplayFor(model => model.OwnerId)
|
||||
</dd>
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.Previsional)
|
||||
@Html.DisplayNameFor(model => model.Provisional)
|
||||
</dt>
|
||||
<dd>
|
||||
@Html.DisplayFor(model => model.Previsional)
|
||||
@Html.DisplayFor(model => model.Provisional)
|
||||
</dd>
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.DateModified)
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Previsional" class="col-md-2 control-label">Prévisionel</label>
|
||||
<label asp-for="Provisional" class="col-md-2 control-label">Prévisionel</label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Previsional" class="form-control" />
|
||||
<span asp-validation-for="Previsional" class="text-danger" ></span>
|
||||
<input asp-for="Provisional" class="form-control" />
|
||||
<span asp-validation-for="Provisional" class="text-danger" ></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
@Html.DisplayNameFor(model => model.OwnerId)
|
||||
</th>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.Previsional)
|
||||
@Html.DisplayNameFor(model => model.Provisional)
|
||||
</th>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.DateModified)
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
@Html.DisplayFor(modelItem => item.OwnerId)
|
||||
</td>
|
||||
<td>
|
||||
@Html.DisplayFor(modelItem => item.Previsional)
|
||||
@Html.DisplayFor(modelItem => item.Provisional)
|
||||
</td>
|
||||
<td>
|
||||
@Html.DisplayFor(modelItem => item.DateModified)
|
||||
|
|
|
|||
|
|
@ -1,25 +1,25 @@
|
|||
@model NominativeServiceCommand
|
||||
|
||||
@if (Model!=null && Model.PaymentId!=null) {
|
||||
|
||||
@if (Model.Regularisation.Executor.Id == User.GetUserId()) {
|
||||
|
||||
@if (Model.Regularization.Executor.Id == User.GetUserId()) {
|
||||
<text>
|
||||
Votre paiment
|
||||
</text>
|
||||
} else {
|
||||
<text>
|
||||
Le paiment de @Html.DisplayFor(m=>m.Regularisation.Executor.UserName)
|
||||
Le paiment de @Html.DisplayFor(m=>m.Regularization.Executor.UserName)
|
||||
</text>
|
||||
}
|
||||
<text> :
|
||||
<text> :
|
||||
|
||||
</text>
|
||||
<label>@Model.GetIsAcquitted()
|
||||
<input type="checkbox" checked="@Model.GetIsAcquitted()" disabled/>
|
||||
<a asp-controller="Manage" asp-action="PaymentInfo" asp-route-id="@Model.Regularisation.CreationToken">@Model.Regularisation.CreationToken</a>
|
||||
<input type="checkbox" checked="@Model.GetIsAcquitted()" disabled/>
|
||||
<a asp-controller="Manage" asp-action="PaymentInfo" asp-route-id="@Model.Regularization.CreationToken">@Model.Regularization.CreationToken</a>
|
||||
</label>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@if (!Model.GetIsAcquitted()) {
|
||||
<div id="paypalzone"></div>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
</dt>
|
||||
<dd>@await Component.InvokeAsync("Bill", Model)
|
||||
</dd>
|
||||
<dt>@Html.DisplayNameFor(m=>m.Regularisation)</dt>
|
||||
<dd>
|
||||
<dt>@Html.DisplayNameFor(m=>m.Regularization)</dt>
|
||||
<dd>
|
||||
@await Component.InvokeAsync("PayPalButton", Model)
|
||||
</dd>
|
||||
</dl>
|
||||
|
|
|
|||
|
|
@ -76,15 +76,21 @@ namespace Yavsc.Helpers
|
|||
{
|
||||
Config.ProfileTypes.Add(c);
|
||||
}
|
||||
if (c.IsClass && !c.IsAbstract &&
|
||||
c.GetInterface(nameof(IBillable)) != null)
|
||||
{
|
||||
BillingService.GlobalBillingMap.Add(c.Name, c.AssemblyQualifiedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var propertyInfo in typeof(ApplicationDbContext).GetProperties())
|
||||
{
|
||||
foreach (var attr in propertyInfo.CustomAttributes)
|
||||
if (propertyInfo.PropertyType.IsGenericType &&
|
||||
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
|
||||
{
|
||||
// something like a DbSet?
|
||||
if (typeof(Yavsc.Attributes.ActivitySettingsAttribute).IsAssignableFrom(attr.AttributeType))
|
||||
var entityType = propertyInfo.PropertyType.GetGenericArguments()[0];
|
||||
if (typeof(IUserSettings).IsAssignableFrom(entityType))
|
||||
{
|
||||
BillingService.UserSettings.Add(propertyInfo);
|
||||
}
|
||||
|
|
@ -94,16 +100,16 @@ namespace Yavsc.Helpers
|
|||
RegisterBilling<HairCutQuery>(BillingCodes.Brush, new Func<ApplicationDbContext, long, IQuery>
|
||||
((db, id) =>
|
||||
{
|
||||
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularisation).Single(q => q.Id == id);
|
||||
var query = db.HairCutQueries.Include(q => q.Prestation).Include(q => q.Regularization).Single(q => q.Id == id);
|
||||
query.SelectedProfile = db.BrusherProfile.Single(b => b.UserId == query.PerformerId);
|
||||
return query;
|
||||
}));
|
||||
|
||||
RegisterBilling<HairMultiCutQuery>(BillingCodes.MBrush, new Func<ApplicationDbContext, long, IQuery>
|
||||
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
((db, id) => db.HairMultiCutQueries.Include(q => q.Regularization).Single(q => q.Id == id)));
|
||||
|
||||
RegisterBilling<RdvQuery>(BillingCodes.Rdv, new Func<ApplicationDbContext, long, IQuery>
|
||||
((db, id) => db.RdvQueries.Include(q => q.Regularisation).Single(q => q.Id == id)));
|
||||
((db, id) => db.RdvQueries.Include(q => q.Regularization).Single(q => q.Id == id)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -290,22 +290,17 @@ namespace Yavsc.Models
|
|||
|
||||
public DbSet<Instrument> Instrument { get; set; }
|
||||
|
||||
[ActivitySettings]
|
||||
public DbSet<DjSettings> DjSettings { get; set; }
|
||||
|
||||
[ActivitySettings]
|
||||
public DbSet<Instrumentation> Instrumentation { get; set; }
|
||||
|
||||
[ActivitySettings]
|
||||
public DbSet<FormationSettings> FormationSettings { get; set; }
|
||||
|
||||
[ActivitySettings]
|
||||
public DbSet<MusicLoverSettings> GeneralSettings { get; set; }
|
||||
public DbSet<MusicLoverSettings> MusicLoverSettings { get; set; }
|
||||
public DbSet<CoWorking> CoWorking { get; set; }
|
||||
|
||||
private void AddTimestamps(string userId)
|
||||
{
|
||||
var entities =
|
||||
private void AddTimestamps(string userId)
|
||||
{ var entities =
|
||||
ChangeTracker.Entries()
|
||||
.Where(x => x.Entity.GetType().GetInterface(nameof(ITrackedEntity)) != null
|
||||
&& (x.State == EntityState.Added || x.State == EntityState.Modified));
|
||||
|
|
@ -360,7 +355,6 @@ namespace Yavsc.Models
|
|||
public DbSet<DismissClicked> DismissClicked { get; set; }
|
||||
|
||||
|
||||
[ActivitySettings]
|
||||
public DbSet<BrusherProfile> BrusherProfile { get; set; }
|
||||
|
||||
public DbSet<BankIdentity> BankIdentity { get; set; }
|
||||
|
|
|
|||
|
|
@ -59,14 +59,14 @@ namespace Yavsc.Models.Billing
|
|||
/// <summary>
|
||||
/// The performer identifier
|
||||
/// </summary>
|
||||
[ForeignKey("PerformerId"),Display(Name="Préstataire")]
|
||||
[ForeignKey("PerformerId"),Display(Name="PerformerProfile")]
|
||||
public PerformerProfile PerformerProfile { get; set; }
|
||||
|
||||
public DateTime? ValidationDate {get; set;}
|
||||
|
||||
|
||||
[Display(Name="Previsional")]
|
||||
public decimal? Previsional { get; set; }
|
||||
[Display(Name="Provisional")]
|
||||
public decimal? Provisional { get; set; }
|
||||
/// <summary>
|
||||
/// The bill
|
||||
/// </summary>
|
||||
|
|
@ -82,7 +82,7 @@ namespace Yavsc.Models.Billing
|
|||
|
||||
public bool GetIsAcquitted()
|
||||
{
|
||||
return Regularisation?.IsOk() ?? false;
|
||||
return Regularization?.IsOk() ?? false;
|
||||
}
|
||||
|
||||
public string GetFileBaseName(IBillingService billingService)
|
||||
|
|
@ -93,11 +93,11 @@ namespace Yavsc.Models.Billing
|
|||
return $"facture-{bcode}-{Id}{ack}";
|
||||
}
|
||||
|
||||
[ForeignKey("Regularisation")]
|
||||
[ForeignKey("Regularization")]
|
||||
|
||||
public string? PaymentId { get; set; }
|
||||
|
||||
[Display(Name = "Acquittement de la facture")]
|
||||
public virtual PayPalPayment Regularisation { get; set; }
|
||||
public virtual PayPalPayment Regularization { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ Prestation.Gender == HairCutGenders.Women ?
|
|||
UserId = ClientId,
|
||||
Avatar = Client.Avatar
|
||||
},
|
||||
Previsional = Previsional,
|
||||
Previsional = Provisional,
|
||||
EventDate = EventDate,
|
||||
Location = Location,
|
||||
Id = Id,
|
||||
|
|
|
|||
|
|
@ -8,33 +8,20 @@ using Yavsc.Billing;
|
|||
|
||||
namespace Yavsc.Models.Haircut
|
||||
{
|
||||
public class HairPrestationCollectionItem {
|
||||
|
||||
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public long Id { get; set; }
|
||||
public long PrestationId { get; set; }
|
||||
[ForeignKeyAttribute("PrestationId")]
|
||||
public virtual HairPrestation Prestation { get; set; }
|
||||
|
||||
public long QueryId { get; set; }
|
||||
|
||||
[ForeignKeyAttribute("QueryId")]
|
||||
public virtual HairMultiCutQuery Query { get; set; }
|
||||
}
|
||||
|
||||
public class HairMultiCutQuery : NominativeServiceCommand
|
||||
{
|
||||
// Bill description
|
||||
string _customDescription = null;
|
||||
public override string Description
|
||||
string _customDescription = null;
|
||||
public override string Description
|
||||
{
|
||||
get {
|
||||
get {
|
||||
return _customDescription ?? "Prestation en coiffure à domicile [commande groupée]" ;
|
||||
}
|
||||
set {
|
||||
_customDescription = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
override public long Id { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace Yavsc.Models.Haircut
|
||||
{
|
||||
public class HairPrestationCollectionItem {
|
||||
|
||||
[Key(), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public long Id { get; set; }
|
||||
public long PrestationId { get; set; }
|
||||
[ForeignKeyAttribute("PrestationId")]
|
||||
public virtual HairPrestation Prestation { get; set; }
|
||||
|
||||
public long QueryId { get; set; }
|
||||
|
||||
[ForeignKeyAttribute("QueryId")]
|
||||
public virtual HairMultiCutQuery Query { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,9 @@ namespace Yavsc.Services
|
|||
new Dictionary<string, Func<ApplicationDbContext, long, IQuery>>();
|
||||
public static List<PropertyInfo> UserSettings = new List<PropertyInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// Mapping from activity codes to IUserSettings
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> GlobalBillingMap =
|
||||
new Dictionary<string, string>();
|
||||
|
||||
|
|
@ -30,7 +33,12 @@ namespace Yavsc.Services
|
|||
return Task.FromResult(GetBillable(DbContext, billingCode, queryId));
|
||||
}
|
||||
|
||||
public static IQuery GetBillable(ApplicationDbContext context, string billingCode, long queryId) => Billing[billingCode](context, queryId);
|
||||
public static IQuery GetBillable(ApplicationDbContext context, string billingCode, long queryId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public async Task<IUserSettings> GetPerformersSettingsAsync(string activityCode, string userId)
|
||||
{
|
||||
var activity = await DbContext.Activities.SingleAsync(a => a.Code == activityCode);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue