diff --git a/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs b/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs index 55e830660..97ed2e61c 100644 --- a/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs +++ b/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs @@ -1,12 +1,9 @@ using System.Net; using System.Net.Http.Headers; -using System.Net.Http.Json; using Microsoft.Extensions.DependencyInjection; using Yavsc.Api.Test.Fixtures; using Yavsc.Helpers; using Yavsc.Models; -using Yavsc.Models.Billing; -using Yavsc.Models.Workflow; using Yavsc.Tests.Shared; namespace Yavsc.Api.Test; @@ -37,88 +34,21 @@ public sealed class FrontOfficeApiControllerTests : IClassFixture JsonBody(new { }); - - private static HttpContent WithSignature() => - JsonBody(new { strokes = SampleStrokes, coordinateMax = 10_000 }); - - private static JsonContent JsonBody(object value) => - JsonContent.Create(value, mediaType: new MediaTypeHeaderValue("application/json")); - - /// - /// Seeds an Rdv query with distinct parties (provider alice, - /// client bob) and, when is true, - /// an estimate linked to it via CommandId. Returns the query id - /// and (when seeded) the estimate id. - /// - private (long queryId, long estimateId) SeedQuery(bool withEstimate) - { - WorkflowHelpers.ConfigureBillingService(); - _fixture.ResetAndSeedActivityGraph(); - - using var scope = _fixture.Services.CreateScope(); - var db = scope.ServiceProvider.GetRequiredService(); - - var location = db.Locations.Single(l => l.Address == "1 rue du Test"); - - var query = new RdvQuery - { - ActivityCode = "dev", - ClientId = "bob", - PerformerId = "alice", - Consent = true, - UserCreated = "alice", - UserModified = "alice", - DateCreated = DateTime.UtcNow, - DateModified = DateTime.UtcNow, - EventDate = DateTime.UtcNow.AddDays(1), - Location = location, - Reason = "Initial rendez-vous", - Status = Yavsc.QueryStatus.Inserted, - }; - db.RdvQueries.Add(query); - db.SaveChanges(); - - long estimateId = 0; - if (withEstimate) - { - var estimate = new Estimate - { - CommandId = query.Id, - ClientId = "bob", - OwnerId = "alice", - CommandType = BillingCodes.Rdv, - Title = "Devis prestation", - Description = "Devis de test", - AttachedFiles = new List(), - AttachedGraphics = new List(), - Bill = new List - { - new() { Name = "Prestation", Description = "Prestation de base", Count = 1, UnitaryCost = 120m, Currency = "EUR" }, - }, - }; - db.Estimates.Add(estimate); - db.SaveChanges(); - estimateId = estimate.Id; - } - - return (query.Id, estimateId); - } - [Fact] public async Task Front_accept_query_updates_status_without_server_error() { - var (queryId, _) = SeedQuery(withEstimate: false); + WorkflowHelpers.ConfigureBillingService(); + _fixture.ResetAndSeedRdvQueryGraph(); + + long queryId; + using (var scope = _fixture.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + queryId = db.RdvQueries.Select(q => q.Id).Single(); + } using var http = NewClient(); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - NoSignature(), - TestContext.Current.CancellationToken); + var response = await http.PostAsync($"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", content: null, TestContext.Current.CancellationToken); var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); @@ -129,118 +59,4 @@ public sealed class FrontOfficeApiControllerTests : IClassFixture(); - - Assert.Equal(QueryStatus.Accepted, assertDb.RdvQueries.Single(q => q.Id == queryId).Status); - - var estimate = assertDb.Estimates.Single(e => e.Id == estimateId); - Assert.True(estimate.ProviderValidationDate != default); - Assert.True(estimate.ClientValidationDate == default); - - var signature = assertDb.Signatures.Single(s => s.EstimateId == estimateId); - Assert.Equal(SignatureType.Pro, signature.Type); - Assert.Equal("alice", signature.SignerId); - } - - [Fact] - public async Task Client_accept_with_signature_sets_ClientValidationDate_and_Client_signature() - { - var (queryId, estimateId) = SeedQuery(withEstimate: true); - - using var http = NewClient(subject: "bob"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); - Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); - - using var assertScope = _fixture.Services.CreateScope(); - var assertDb = assertScope.ServiceProvider.GetRequiredService(); - - Assert.Equal(QueryStatus.Accepted, assertDb.RdvQueries.Single(q => q.Id == queryId).Status); - - var estimate = assertDb.Estimates.Single(e => e.Id == estimateId); - Assert.True(estimate.ClientValidationDate != default); - Assert.True(estimate.ProviderValidationDate == default); - - var signature = assertDb.Signatures.Single(s => s.EstimateId == estimateId); - Assert.Equal(SignatureType.Client, signature.Type); - Assert.Equal("bob", signature.SignerId); - } - - [Fact] - public async Task Reject_sets_status_Rejected() - { - var (queryId, _) = SeedQuery(withEstimate: false); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/reject?billingCode=Rdv&queryId={queryId}", - NoSignature(), - TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); - Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); - - using var assertScope = _fixture.Services.CreateScope(); - var assertDb = assertScope.ServiceProvider.GetRequiredService(); - Assert.Equal(QueryStatus.Rejected, assertDb.RdvQueries.Single(q => q.Id == queryId).Status); - } - - [Fact] - public async Task Non_party_actor_is_forbidden() - { - var (queryId, _) = SeedQuery(withEstimate: false); - - using var http = NewClient(subject: "carol"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - NoSignature(), - TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); - } - - [Fact] - public async Task Accept_with_signature_but_no_estimate_returns_400() - { - var (queryId, _) = SeedQuery(withEstimate: false); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - } - - [Fact] - public async Task Reject_with_signature_returns_400() - { - var (queryId, _) = SeedQuery(withEstimate: true); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/reject?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - } -} \ No newline at end of file +} diff --git a/src/Yavsc.Api/Controllers/Business/BillingController.cs b/src/Yavsc.Api/Controllers/Business/BillingController.cs index ba43094a9..ba099e7c7 100644 --- a/src/Yavsc.Api/Controllers/Business/BillingController.cs +++ b/src/Yavsc.Api/Controllers/Business/BillingController.cs @@ -401,12 +401,79 @@ namespace Yavsc.ApiControllers Strokes = body.Strokes, }; - // The Signature row is staged by the shared persister; we - // SaveChanges here so the signature write is one - // transaction. The Strokes column is the source of truth — - // there is no on-disk copy to roll back on failure. - var signature = await EstimateSignaturePersister.StageAsync( - dbContext, id, type, userId, payload, token); + // Disk write first: a disk failure shouldn't leave + // a Signature row pointing at a file that doesn't + // exist. The file helper throws on filesystem + // problems and propagates here. + FileReceivedInfo fi; + try + { + fi = await User.ReceiveEstimateSignatureAsync(id, type, payload, + siteSettings, token); + } + catch (Exception ex) + { + logger.LogError(ex, "estimate {Id}: signature file write failed", id); + return BadRequest(new { Error = "file write failed", Detail = ex.Message }); + } + + // Find-or-add: the (EstimateId, Type) pair is + // unique, so a second POST for the same side of the + // estimate replaces the previous signature. EF + // translates this into a single UPDATE when the + // row exists and an INSERT otherwise; the unique + // index in ApplicationDbContext is the + // database-level guarantee that the contract + // holds if two requests race. + var signature = await dbContext.Signatures + .FirstOrDefaultAsync(s => s.EstimateId == id && s.Type == type, token); + + if (signature is null) + { + signature = new Signature + { + EstimateId = id, + SignerId = userId, + Type = type, + }; + dbContext.Signatures.Add(signature); + } + else + { + // Roll the signer's quota back by the size of + // the file we're about to orphan: the old + // FilePath is no longer referenced once we + // overwrite FilePath below. + try + { + var orphan = new FileInfo(signature.FilePath); + if (orphan.Exists) + { + var signerForOrphan = await dbContext.Users + .FirstOrDefaultAsync(u => u.Id == userId, token); + if (signerForOrphan is not null) + signerForOrphan.DiskUsage = + Math.Max(0, signerForOrphan.DiskUsage - orphan.Length); + } + } + catch { /* best effort — the file is being replaced anyway */ } + } + + signature.SignerId = userId; + signature.CoordinateMax = payload.CoordinateMax; + signature.Strokes = payload.Strokes; + signature.CapturedAtUtc = payload.CapturedAtUtc; + signature.FilePath = Path.Combine(fi.DestDir, fi.FileName); + + // Bump the signer's quota. The Signature row's + // SignerId is the IdentityUser.Id (a string), so we + // look up by Id and not by username. + var signer = await dbContext.Users + .FirstOrDefaultAsync(u => u.Id == userId, token); + if (signer is not null) + { + signer.DiskUsage += new FileInfo(signature.FilePath).Length; + } try { @@ -415,6 +482,10 @@ namespace Yavsc.ApiControllers catch (Exception ex) { logger.LogError(ex, "estimate {Id}: signature db write failed", id); + // Best-effort rollback: remove the file we wrote + // so disk and db don't disagree. + try { System.IO.File.Delete(signature.FilePath); } + catch { /* swallow — the row will be re-orphaned, the user re-signs */ } return BadRequest(new { Error = "db write failed", Detail = ex.Message }); } diff --git a/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs b/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs index 5f328728c..3adac5b03 100644 --- a/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs +++ b/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs @@ -1,10 +1,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.EntityFrameworkCore; -using Yavsc.Abstract.Workflow; using Yavsc.Helpers; using Yavsc.Models; -using Yavsc.Models.Billing; using Yavsc.Services; using Yavsc.Server.Helpers; using Yavsc.ViewModels.FrontOffice; @@ -16,17 +13,13 @@ namespace Yavsc.ApiControllers public class FrontOfficeApiController : Controller { ApplicationDbContext dbContext; - private readonly IBillingService billing; - private readonly ILogger logger; - public FrontOfficeApiController( - ApplicationDbContext context, - ILoggerFactory loggerFactory, - IBillingService billing = null) + private IBillingService billing; + + public FrontOfficeApiController(ApplicationDbContext context, IBillingService billing = null) { dbContext = context; this.billing = billing ?? new BillingService(context); - logger = loggerFactory.CreateLogger(); } [HttpGet("profiles/{actCode}")] @@ -35,183 +28,29 @@ namespace Yavsc.ApiControllers return await dbContext.ListPerformersAsync(billing, actCode); } - // POST: api/front/query/accept - // - // Either party of the query may accept: the provider - // (PerformerId) accepts the client's request, or the client - // (ClientId) accepts the provider's estimate. An optional - // signature (the PostIt wire format) may be sent along; it is - // persisted on the estimate linked to the query - // (Estimate.CommandId == queryId), and the matching validation - // date is stamped — ProviderValidationDate when the provider - // accepts, ClientValidationDate when the client accepts. - [HttpPost("query/accept")] - public async Task AcceptQuery( - string billingCode, - long queryId, - [FromBody] QueryAcceptanceRequest? body, - CancellationToken token) - { - var (query, role, error) = await ResolveAndAuthorizeAsync(billingCode, queryId, token); - if (error is not null) return error; - if (query is null) return BadRequest(new { Error = "query not found" }); - - Signature? signature = null; - if (HasSignature(body)) - { - // Admins bypass authorization but are neither party of - // the estimate, so they cannot sign. - if (role == ActorRole.Admin) - return BadRequest(new { Error = "admin cannot sign on behalf of a party" }); - - var estimate = await dbContext.Estimates - .Where(e => e.CommandId == queryId) - .OrderByDescending(e => e.Id) - .FirstOrDefaultAsync(token); - if (estimate is null) - return BadRequest(new { Error = "no estimate linked to this query" }); - - var type = role == ActorRole.Provider - ? SignatureType.Pro - : SignatureType.Client; - - var payload = new SignaturePadPayload - { - CoordinateMax = body!.CoordinateMax, - CapturedAtUtc = body.CapturedAtUtc ?? DateTime.UtcNow, - Strokes = body.Strokes!, - }; - - try - { - var uid = User.GetUserId(); - signature = await EstimateSignaturePersister.StageAsync( - dbContext, estimate.Id, type, uid, payload, token); - } - catch (Exception ex) - { - logger.LogError(ex, "query {QueryId}: signature stage failed", queryId); - return BadRequest(new { Error = "signature stage failed", Detail = ex.Message }); - } - - if (role == ActorRole.Provider) - estimate.ProviderValidationDate = DateTime.UtcNow; - else - estimate.ClientValidationDate = DateTime.UtcNow; - } - - query.Status = QueryStatus.Accepted; - - try - { - await dbContext.SaveChangesAsync(User.GetUserId(), token); - } - catch (Exception ex) - { - logger.LogError(ex, "query {QueryId}: accept db write failed", queryId); - return BadRequest(new { Error = "db write failed", Detail = ex.Message }); - } - - return Ok(new - { - queryId, - status = query.Status.ToString(), - signature = signature is null - ? null - : new - { - id = signature.Id, - estimateId = signature.EstimateId, - type = signature.Type.ToString(), - }, - }); - } - - // POST: api/front/query/reject - // - // Either party may reject. A signature is not meaningful on a - // rejection, so one is refused with 400. [HttpPost("query/reject")] - public async Task RejectQuery( - string billingCode, - long queryId, - [FromBody] QueryAcceptanceRequest? body, - CancellationToken token) + public IActionResult RejectQuery(string billingCode, long queryId) { - if (HasSignature(body)) - return BadRequest(new { Error = "signature not allowed on reject" }); + if (billingCode == null) return BadRequest("billingCode"); + if (queryId == 0) return BadRequest("queryId"); + var billing = BillingService.GetBillable(dbContext, billingCode, queryId); + if (billing == null) return BadRequest(); - var (query, role, error) = await ResolveAndAuthorizeAsync(billingCode, queryId, token); - if (error is not null) return error; - if (query is null) return BadRequest(new { Error = "query not found" }); - - query.Status = QueryStatus.Rejected; - await dbContext.SaveChangesAsync(User.GetUserId(), token); - - return Ok(new - { - queryId, - status = query.Status.ToString(), - }); + billing.Status = QueryStatus.Rejected; + dbContext.SaveChanges(User.GetUserId()); + return Ok(); } - // Loads the query and checks that the caller is a party to it - // (provider or client) or an admin. Returns the role the caller - // is acting in; Admin is only returned when the caller is - // neither party but is an admin. - private async Task<(IQuery? Query, ActorRole Role, IActionResult? Error)> - ResolveAndAuthorizeAsync(string billingCode, long queryId, CancellationToken token) + [HttpPost("query/accept")] + public IActionResult AcceptQuery(string billingCode, long queryId) { - if (string.IsNullOrWhiteSpace(billingCode)) - return (null, ActorRole.None, BadRequest("billingCode")); - if (queryId == 0) - return (null, ActorRole.None, BadRequest("queryId")); - - var query = await billing.GetBillAsync(billingCode, queryId); - if (query is null) - return (null, ActorRole.None, BadRequest()); - - var uid = User.GetUserId(); - bool isProvider = query.PerformerId == uid; - bool isClient = query.ClientId == uid; - - if (isProvider) return (query, ActorRole.Provider, null); - if (isClient) return (query, ActorRole.Client, null); - - if (User.IsInRole(Constants.AdminGroupName)) - return (query, ActorRole.Admin, null); - - return (null, ActorRole.None, Forbid()); + if (billingCode == null) return BadRequest("billingCode"); + if (queryId == 0) return BadRequest("queryId"); + var billing = BillingService.GetBillable(dbContext, billingCode, queryId); + if (billing == null) return BadRequest(); + billing.Status = QueryStatus.Accepted; + dbContext.SaveChanges(User.GetUserId()); + return Ok(); } - - private static bool HasSignature(QueryAcceptanceRequest? body) - => body is not null && body.Strokes is not null && body.Strokes.Length > 0; } - - internal enum ActorRole { None, Provider, Client, Admin } } - -namespace Yavsc.ViewModels.FrontOffice -{ - /// - /// Body of POST /api/v1/front/query/accept and - /// .../reject. The field is optional: - /// when present (non-empty) on accept, it is the PostIt wire-format - /// signature captured with the acceptance. All fields are optional - /// so a caller may post {} to accept/reject without a - /// signature. - /// - public class QueryAcceptanceRequest - { - /// - /// Wire-format strokes. See - /// PostIt.Models.SignaturePadData. Null or empty means - /// no signature. - /// - public int[]? Strokes { get; set; } - - public int CoordinateMax { get; set; } = 10_000; - - public DateTime? CapturedAtUtc { get; set; } - } -} \ No newline at end of file diff --git a/src/Yavsc.Org.Tests/EstimateSignatureFileHelperTests.cs b/src/Yavsc.Org.Tests/EstimateSignatureFileHelperTests.cs new file mode 100644 index 000000000..b7b4d3591 --- /dev/null +++ b/src/Yavsc.Org.Tests/EstimateSignatureFileHelperTests.cs @@ -0,0 +1,136 @@ +using System.Security.Claims; +using System.Text.Json; +using Xunit; +using Yavsc.Models.Billing; +using Yavsc.Server.Helpers; +using Yavsc.Server.Models.FileSystem; + +namespace Yavsc.Org.Tests; + +/// +/// Tests for the static +/// helper. Scope is intentionally narrow: the file-naming format, +/// the strokes counter, and the on-disk write path. The controller +/// (authz, db persistence, signalR notification) is out of scope +/// for this commit and will get a dedicated integration test once +/// the Yavsc.Api test project is set up. +/// +public class EstimateSignatureFileHelperTests : IDisposable +{ + private readonly string _tempRoot; + private SiteSettings siteSettings; + + public EstimateSignatureFileHelperTests() + { + // UserFilesDirName is a process-wide static; we redirect + // it to a per-test temp dir so concurrent tests don't + // collide and the host filesystem is not littered. + _tempRoot = Path.Combine( + Path.GetTempPath(), + "yavsc-sig-tests-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempRoot); + this.siteSettings = new SiteSettings { Blog = _tempRoot }; + } + + public void Dispose() + { + try { Directory.Delete(_tempRoot, recursive: true); } + catch { /* best effort — the OS will clean Temp eventually */ } + } + + [Fact] + public void FileNameFormat_lowercases_type_and_includes_estimateId_and_ticks() + { + var name = EstimateSignatureFileHelper.FileNameFormat( + SignatureType.Pro, 42, 638_000_000_000_000_000L); + Assert.Equal("sign-pro-42-638000000000000000.json", name); + + var cli = EstimateSignatureFileHelper.FileNameFormat( + SignatureType.Client, 7, 1L); + Assert.Equal("sign-client-7-1.json", cli); + } + + [Theory] + [InlineData(new int[] { }, 0)] + [InlineData(new[] { 1, 100, 200 }, 1)] + [InlineData(new[] { 2, 1, 2, 3, 4 }, 1)] + [InlineData(new[] { 1, 1, 1, 2, 2, 3, 3 }, 2)] + [InlineData(new[] { 0, 1, 2, 3 }, 0)] // malformed k=0: short-circuit + public void ReceiveEstimateSignatureAsync_writes_a_v1_envelope(int[] strokes, int expectedStrokeCount) + { + // We don't read the count back from the helper (it's a + // private method), but the JSON envelope must reflect + // it; this verifies the public behaviour end-to-end. + _ = expectedStrokeCount; + // Arrange + var user = MakeUser("alice"); + var payload = new SignaturePadPayload + { + CoordinateMax = 10_000, + CapturedAtUtc = new DateTime(2026, 7, 4, 12, 0, 0, DateTimeKind.Utc), + Strokes = strokes, + }; + + // Act + var fi = Run(user, 123L, SignatureType.Pro, payload, this.siteSettings); + + // Assert: file exists, sits under the user's root, and + // parses as a yavsc.signature/v1 envelope. + var fullPath = Path.Combine(fi.DestDir, fi.FileName); + Assert.True(File.Exists(fullPath), $"missing: {fullPath}"); + + using var doc = JsonDocument.Parse(File.ReadAllText(fullPath)); + var root = doc.RootElement; + Assert.Equal("yavsc.signature/v1", root.GetProperty("format").GetString()); + Assert.Equal(10_000, root.GetProperty("coordinateMax").GetInt32()); + Assert.Equal(123L, root.GetProperty("estimateId").GetInt64()); + Assert.Equal("Pro", root.GetProperty("type").GetString()); + Assert.Equal("alice", root.GetProperty("signerName").GetString()); + Assert.Equal(expectedStrokeCount, root.GetProperty("strokeCount").GetInt32()); + } + + [Fact] + public async Task ReceiveEstimateSignatureAsync_rejects_null_payload() + { + var user = MakeUser("bob"); + // Capture TestContext.Current.CancellationToken outside the + // lambda so xUnit1051 sees a real CancellationToken argument + // (the lambda body runs on a different stack frame). + var ct = TestContext.Current.CancellationToken; + await Assert.ThrowsAsync(() => + EstimateSignatureFileHelper.ReceiveEstimateSignatureAsync( + user, 1L, SignatureType.Pro, payload: null!, + siteSettings, token: ct)); + } + + [Fact] + public async Task ReceiveEstimateSignatureAsync_rejects_non_positive_estimateId() + { + var user = MakeUser("bob"); + var payload = new SignaturePadPayload { Strokes = new[] { 1, 100, 100 } }; + var ct = TestContext.Current.CancellationToken; + await Assert.ThrowsAsync(() => + EstimateSignatureFileHelper.ReceiveEstimateSignatureAsync( + user, 0L, SignatureType.Pro, payload, this.siteSettings, token: ct)); + } + + // --- helpers ---------------------------------------------------- + + private static FileReceivedInfo Run( + ClaimsPrincipal user, long estimateId, SignatureType type, SignaturePadPayload payload, SiteSettings siteSettings) + { + // The helper is async; tests that don't care about the + // result can call it sync via .GetAwaiter().GetResult() + // because we know it never throws in the happy path. + return EstimateSignatureFileHelper + .ReceiveEstimateSignatureAsync(user, estimateId, type, payload, siteSettings, CancellationToken.None) + .GetAwaiter().GetResult(); + } + + private static ClaimsPrincipal MakeUser(string username) + { + return new ClaimsPrincipal(new ClaimsIdentity( + new[] { new Claim(ClaimTypes.Name, username) }, + authenticationType: "test")); + } +} diff --git a/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs b/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs index 4a7600596..5e7ab7776 100644 --- a/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs +++ b/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs @@ -3,7 +3,6 @@ using System.Net.Http; using IdentityServer8.Stores; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; -using Yavsc.Tests.Shared; using Xunit; namespace Yavsc.Org.Tests.Smoke; @@ -66,26 +65,4 @@ public class AccountSmokeTests : IClassFixture Assert.Equal("/signin", location.AbsolutePath); Assert.Contains("ReturnUrl=%2FEstimate", location.Query); } - - // Regression: the POST Delete action existed, but neither the GET - // Delete view nor a rendered confirmation page was reachable — - // Views/Account/Delete.cshtml was missing. An authenticated GET - // must now return the confirmation page (200). - [Fact] - public async Task Authenticated_get_account_delete_returns_confirmation_page() - { - var client = _factory.CreateClient(new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false, - }); - client.DefaultRequestHeaders.Add( - TestAuthPolicyProvider.HeaderName, TestAuthPolicyProvider.AdminRole); - - var response = await client.GetAsync("/Account/Delete", TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - var body = await response.Content.ReadAsStringAsync( - TestContext.Current.CancellationToken); - Assert.Contains("Supprimer mon compte", body); - } } diff --git a/src/Yavsc.Org/Controllers/Accounting/AccountController.cs b/src/Yavsc.Org/Controllers/Accounting/AccountController.cs index f8fa7f0d2..04f60aa1d 100644 --- a/src/Yavsc.Org/Controllers/Accounting/AccountController.cs +++ b/src/Yavsc.Org/Controllers/Accounting/AccountController.cs @@ -1061,7 +1061,6 @@ IHtmlLocalizerFactory htmlLocalizerFactory, } return View(); } - [HttpPost, Authorize] public async Task Delete(UnregisterViewModel model) { diff --git a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.Designer.cs b/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.Designer.cs deleted file mode 100644 index 04b9fdc68..000000000 --- a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.Designer.cs +++ /dev/null @@ -1,4713 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Yavsc.Models; - -#nullable disable - -namespace Yavsc.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20260921145454_DropSignatureFilePath")] - partial class DropSignatureFilePath - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("AllowedAccessTokenSigningAlgorithms") - .HasColumnType("text"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("ApiResources"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Scope") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceSecrets"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("ApiScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("ScopeId1") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.HasIndex("ScopeId1"); - - b.ToTable("ApiScopeClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("ScopeId1") - .HasColumnType("integer"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.HasIndex("ScopeId1"); - - b.ToTable("ApiScopeProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("AbsoluteRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenType") - .HasColumnType("integer"); - - b.Property("AllowAccessTokensViaBrowser") - .HasColumnType("boolean"); - - b.Property("AllowOfflineAccess") - .HasColumnType("boolean"); - - b.Property("AllowPlainTextPkce") - .HasColumnType("boolean"); - - b.Property("AllowRememberConsent") - .HasColumnType("boolean"); - - b.Property("AllowedIdentityTokenSigningAlgorithms") - .HasColumnType("text"); - - b.Property("AlwaysIncludeUserClaimsInIdToken") - .HasColumnType("boolean"); - - b.Property("AlwaysSendClientClaims") - .HasColumnType("boolean"); - - b.Property("AuthorizationCodeLifetime") - .HasColumnType("integer"); - - b.Property("BackChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("BackChannelLogoutUri") - .HasColumnType("text"); - - b.Property("ClientClaimsPrefix") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("ClientName") - .HasColumnType("text"); - - b.Property("ClientUri") - .HasColumnType("text"); - - b.Property("ConsentLifetime") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DeviceCodeLifetime") - .HasColumnType("integer"); - - b.Property("EnableLocalLogin") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutUri") - .HasColumnType("text"); - - b.Property("IdentityTokenLifetime") - .HasColumnType("integer"); - - b.Property("IncludeJwtId") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("LogoUri") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("PairWiseSubjectSalt") - .HasColumnType("text"); - - b.Property("ProtocolType") - .HasColumnType("text"); - - b.Property("RefreshTokenExpiration") - .HasColumnType("integer"); - - b.Property("RefreshTokenUsage") - .HasColumnType("integer"); - - b.Property("RequireClientSecret") - .HasColumnType("boolean"); - - b.Property("RequireConsent") - .HasColumnType("boolean"); - - b.Property("RequirePkce") - .HasColumnType("boolean"); - - b.Property("RequireRequestObject") - .HasColumnType("boolean"); - - b.Property("SlidingRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("UpdateAccessTokenClaimsOnRefresh") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.Property("UserCodeType") - .HasColumnType("text"); - - b.Property("UserSsoLifetime") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("Clients"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Origin") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientCorsOrigins"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("GrantType") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientGrantTypes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Provider") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientIdPRestrictions"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("PostLogoutRedirectUri") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientPostLogoutRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("RedirectUri") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Scope") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientSecrets"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b => - { - b.Property("UserCode") - .HasColumnType("text"); - - b.Property("DeviceCode") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasColumnType("text"); - - b.Property("SubjectId") - .HasColumnType("text"); - - b.HasKey("UserCode", "DeviceCode"); - - b.ToTable("DeviceFlowCodes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("IdentityResources"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b => - { - b.Property("Key") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("ConsumedTime") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasColumnType("text"); - - b.Property("SubjectId") - .HasColumnType("text"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Key"); - - b.ToTable("PersistedGrants"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("text"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("Yavsc.Abstract.Identity.ClientProviderInfo", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Avatar") - .HasColumnType("text"); - - b.Property("BillingAddressId") - .HasColumnType("bigint"); - - b.Property("EMail") - .HasColumnType("text"); - - b.Property("Phone") - .HasColumnType("text"); - - b.Property("UserName") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("ClientProviderInfo"); - }); - - modelBuilder.Entity("Yavsc.Abstract.Models.Messaging.Notification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Target") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("body") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("click_action") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("color") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("icon") - .ValueGeneratedOnAdd() - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasDefaultValue("exclam"); - - b.Property("sound") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("tag") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("title") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.HasKey("Id"); - - b.ToTable("Notification"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.Ban", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.Property("TargetId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("TargetId"); - - b.ToTable("Ban"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.BlackListed", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.HasIndex("UserId"); - - b.ToTable("BlackListed"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToBlogPost", b => - { - b.Property("CircleId") - .HasColumnType("bigint"); - - b.Property("BlogPostId") - .HasColumnType("bigint"); - - b.HasKey("CircleId", "BlogPostId"); - - b.HasIndex("BlogPostId"); - - b.ToTable("CircleAuthorizationToBlogPost"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToFile", b => - { - b.Property("CircleId") - .HasColumnType("bigint"); - - b.Property("Path") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Access") - .HasColumnType("smallint"); - - b.HasKey("CircleId", "Path", "OwnerId"); - - b.HasIndex("OwnerId"); - - b.ToTable("CircleAuthorizationToFile"); - }); - - modelBuilder.Entity("Yavsc.Models.AccountBalance", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("ContactCredits") - .HasColumnType("bigint"); - - b.Property("Credits") - .HasColumnType("numeric"); - - b.HasKey("UserId"); - - b.ToTable("BankStatus"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("AllowMonthlyEmail") - .HasColumnType("boolean"); - - b.Property("Avatar") - .ValueGeneratedOnAdd() - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasDefaultValue("/images/Users/icon_user.png"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("DedicatedGoogleCalendar") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("DiskQuota") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(524288000L); - - b.Property("DiskUsage") - .HasColumnType("bigint"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("FullName") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("MaxFileSize") - .HasColumnType("bigint"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("PostalAddressId") - .HasColumnType("bigint"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasAlternateKey("Email"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PostalAddressId"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("Yavsc.Models.BalanceImpact", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BalanceId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ExecDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Impact") - .HasColumnType("numeric"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("BalanceId"); - - b.ToTable("BalanceImpact"); - }); - - modelBuilder.Entity("Yavsc.Models.Bank.BankIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AccountNumber") - .IsRequired() - .HasColumnType("text"); - - b.Property("BIC") - .IsRequired() - .HasColumnType("text"); - - b.Property("BankCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("BankedKey") - .HasColumnType("integer"); - - b.Property("IBAN") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("WicketCode") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("BankIdentity"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.CommandLine", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Count") - .HasColumnType("integer"); - - b.Property("Currency") - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("EstimateId") - .HasColumnType("bigint"); - - b.Property("EstimateTemplateId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("UnitaryCost") - .HasColumnType("numeric"); - - b.HasKey("Id"); - - b.HasIndex("EstimateId"); - - b.HasIndex("EstimateTemplateId"); - - b.ToTable("CommandLine"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AttachedFilesString") - .IsRequired() - .HasColumnType("text"); - - b.Property("AttachedGraphicsString") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientValidationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("CommandId") - .HasColumnType("bigint"); - - b.Property("CommandType") - .IsRequired() - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ProviderValidationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("CommandId"); - - b.HasIndex("OwnerId"); - - b.ToTable("Estimates"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.EstimateTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("EstimateTemplates"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.ExceptionSIREN", b => - { - b.Property("SIREN") - .HasColumnType("text"); - - b.HasKey("SIREN"); - - b.ToTable("ExceptionsSIREN"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.NominativeServiceCommand", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ActivityCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Consent") - .HasColumnType("boolean"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("Discriminator") - .IsRequired() - .HasMaxLength(34) - .HasColumnType("character varying(34)"); - - b.Property("PaymentId") - .HasColumnType("text"); - - b.Property("PerformerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Provisional") - .HasColumnType("numeric"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("UserCreated") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserModified") - .IsRequired() - .HasColumnType("text"); - - b.Property("ValidationDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ActivityCode"); - - b.HasIndex("ClientId"); - - b.HasIndex("PaymentId"); - - b.HasIndex("PerformerId"); - - b.ToTable("NominativeServiceCommands"); - - b.HasDiscriminator("Discriminator").HasValue("NominativeServiceCommand"); - - b.UseTphMappingStrategy(); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Signature", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CapturedAtUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("CoordinateMax") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(10000); - - b.Property("EstimateId") - .HasColumnType("bigint"); - - b.Property("SignerId") - .IsRequired() - .HasColumnType("text"); - - b.PrimitiveCollection("Strokes") - .IsRequired() - .HasColumnType("integer[]"); - - b.Property("Type") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("SignerId"); - - b.HasIndex("EstimateId", "Type") - .IsUnique(); - - b.ToTable("Signatures"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogAttachedFile", b => - { - b.Property("FileId") - .HasColumnType("bigint"); - - b.Property("PostId") - .HasColumnType("bigint"); - - b.HasKey("FileId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("BlogAttachedFiles"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Article") - .HasMaxLength(56224) - .HasColumnType("character varying(56224)"); - - b.Property("AuthorId") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Photo") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.ToTable("BlogSpot"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogTag", b => - { - b.Property("PostId") - .HasColumnType("bigint"); - - b.Property("TagId") - .HasColumnType("bigint"); - - b.HasKey("PostId", "TagId"); - - b.HasIndex("TagId"); - - b.ToTable("BlogTag"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Article") - .IsRequired() - .HasColumnType("text"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ParentId") - .HasColumnType("bigint"); - - b.Property("ReceiverId") - .HasColumnType("bigint"); - - b.Property("UserCreated") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserModified") - .IsRequired() - .HasColumnType("text"); - - b.Property("Visible") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.HasIndex("ParentId"); - - b.HasIndex("ReceiverId"); - - b.ToTable("Comment"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.UploadedFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContentType") - .HasColumnType("text"); - - b.Property("Length") - .HasColumnType("bigint"); - - b.Property("Path") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("UploadedFiles"); - }); - - modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => - { - b.Property("PostId") - .HasColumnType("bigint"); - - b.HasKey("PostId"); - - b.ToTable("blogSpotPublications"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.Property("OwnerId") - .HasColumnType("text"); - - b.HasKey("OwnerId"); - - b.ToTable("Schedule"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.ScheduledEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("PeriodEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("PeriodStart") - .HasColumnType("timestamp with time zone"); - - b.Property("Reccurence") - .HasColumnType("integer"); - - b.Property("ScheduleOwnerId") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScheduleOwnerId"); - - b.HasIndex("PeriodStart", "PeriodEnd"); - - b.ToTable("ScheduledEvent"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatConnection", b => - { - b.Property("ConnectionId") - .HasColumnType("text"); - - b.Property("ApplicationUserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Connected") - .HasColumnType("boolean"); - - b.Property("UserAgent") - .HasColumnType("text"); - - b.HasKey("ConnectionId"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("ChatConnection"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.Property("Name") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LatestJoinPart") - .HasColumnType("timestamp with time zone"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Topic") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Name"); - - b.HasIndex("OwnerId"); - - b.ToTable("ChatRoom"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoomAccess", b => - { - b.Property("ChannelName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Level") - .HasColumnType("integer"); - - b.HasKey("ChannelName", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("ChatRoomAccess"); - }); - - modelBuilder.Entity("Yavsc.Models.Cratie.Option", b => - { - b.Property("Code") - .HasColumnType("text"); - - b.Property("CodeScrutin") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Code", "CodeScrutin"); - - b.ToTable("Option"); - }); - - modelBuilder.Entity("Yavsc.Models.Drawing.Color", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Blue") - .HasColumnType("smallint"); - - b.Property("Green") - .HasColumnType("smallint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Red") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.ToTable("Color"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("ActionDistance") - .HasColumnType("integer"); - - b.Property("CarePrice") - .HasColumnType("numeric"); - - b.Property("FlatFeeDiscount") - .HasColumnType("numeric"); - - b.Property("HalfBalayagePrice") - .HasColumnType("numeric"); - - b.Property("HalfBrushingPrice") - .HasColumnType("numeric"); - - b.Property("HalfColorPrice") - .HasColumnType("numeric"); - - b.Property("HalfDefrisPrice") - .HasColumnType("numeric"); - - b.Property("HalfFoldingPrice") - .HasColumnType("numeric"); - - b.Property("HalfMechPrice") - .HasColumnType("numeric"); - - b.Property("HalfMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("HalfPermanentPrice") - .HasColumnType("numeric"); - - b.Property("KidCutPrice") - .HasColumnType("numeric"); - - b.Property("LongBalayagePrice") - .HasColumnType("numeric"); - - b.Property("LongBrushingPrice") - .HasColumnType("numeric"); - - b.Property("LongColorPrice") - .HasColumnType("numeric"); - - b.Property("LongDefrisPrice") - .HasColumnType("numeric"); - - b.Property("LongFoldingPrice") - .HasColumnType("numeric"); - - b.Property("LongMechPrice") - .HasColumnType("numeric"); - - b.Property("LongMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("LongPermanentPrice") - .HasColumnType("numeric"); - - b.Property("ManBrushPrice") - .HasColumnType("numeric"); - - b.Property("ManCutPrice") - .HasColumnType("numeric"); - - b.Property("ScheduleOwnerId") - .HasColumnType("text"); - - b.Property("ShampooPrice") - .HasColumnType("numeric"); - - b.Property("ShortBalayagePrice") - .HasColumnType("numeric"); - - b.Property("ShortBrushingPrice") - .HasColumnType("numeric"); - - b.Property("ShortColorPrice") - .HasColumnType("numeric"); - - b.Property("ShortDefrisPrice") - .HasColumnType("numeric"); - - b.Property("ShortFoldingPrice") - .HasColumnType("numeric"); - - b.Property("ShortMechPrice") - .HasColumnType("numeric"); - - b.Property("ShortMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("ShortPermanentPrice") - .HasColumnType("numeric"); - - b.Property("WomenHalfCutPrice") - .HasColumnType("numeric"); - - b.Property("WomenLongCutPrice") - .HasColumnType("numeric"); - - b.Property("WomenShortCutPrice") - .HasColumnType("numeric"); - - b.HasKey("UserId"); - - b.HasIndex("ScheduleOwnerId"); - - b.ToTable("BrusherProfile"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Cares") - .HasColumnType("boolean"); - - b.Property("Cut") - .HasColumnType("boolean"); - - b.Property("Dressing") - .HasColumnType("integer"); - - b.Property("Gender") - .HasColumnType("integer"); - - b.Property("Length") - .HasColumnType("integer"); - - b.Property("Shampoo") - .HasColumnType("boolean"); - - b.Property("Tech") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("HairPrestation"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestationCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.Property("QueryId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("PrestationId"); - - b.HasIndex("QueryId"); - - b.ToTable("HairPrestationCollectionItem"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Brand") - .HasColumnType("text"); - - b.Property("ColorId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ColorId"); - - b.ToTable("HairTaint"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaintInstance", b => - { - b.Property("TaintId") - .HasColumnType("bigint"); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.HasKey("TaintId", "PrestationId"); - - b.HasIndex("PrestationId"); - - b.ToTable("HairTaintInstance"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Evolution.Feature", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("ShortName") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("Feature"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Fixing.Bug", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .IsRequired() - .HasMaxLength(10240) - .HasColumnType("character varying(10240)"); - - b.Property("FeatureId") - .HasColumnType("bigint"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("FeatureId"); - - b.ToTable("Bug"); - }); - - modelBuilder.Entity("Yavsc.Models.Identity.DeviceDeclaration", b => - { - b.Property("DeviceId") - .HasColumnType("text"); - - b.Property("DeclarationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("LOCALTIMESTAMP"); - - b.Property("DeviceOwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestActivityUpdate") - .HasColumnType("timestamp with time zone"); - - b.Property("Model") - .IsRequired() - .HasColumnType("text"); - - b.Property("Platform") - .IsRequired() - .HasColumnType("text"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("DeviceId"); - - b.HasIndex("DeviceOwnerId"); - - b.ToTable("DeviceDeclaration"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.DeclarationFlag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DeclarationId") - .HasColumnType("bigint"); - - b.Property("MatchExcerpt") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PatternId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("DeclarationId"); - - b.HasIndex("PatternId"); - - b.ToTable("DeclarationFlag"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.ModerationLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Action") - .HasColumnType("integer"); - - b.Property("DeclarationId") - .HasColumnType("bigint"); - - b.Property("ModeratorId") - .HasColumnType("text"); - - b.Property("ScoreDelta") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("DeclarationId"); - - b.HasIndex("ModeratorId"); - - b.HasIndex("Timestamp"); - - b.ToTable("ModerationLogs", t => - { - t.HasCheckConstraint("CK_ModerationLog_Immutable", "1=1"); - }); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.RegexAlertPattern", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("IsActive") - .HasColumnType("boolean"); - - b.Property("Pattern") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("Severity") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("IsActive"); - - b.ToTable("RegexAlertPatterns"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("DeclarantTokenId") - .HasColumnType("uuid"); - - b.Property("ScoreDelta") - .HasColumnType("integer"); - - b.Property("Sentiment") - .HasColumnType("integer"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("SubmittedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("TrustTokenId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("Status"); - - b.HasIndex("SubmittedAt"); - - b.HasIndex("TrustTokenId"); - - b.ToTable("TrustDeclarations"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("TokenSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b.Property("TrustScore") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("TokenHash") - .IsUnique(); - - b.ToTable("TrustTokens"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Depth") - .HasColumnType("numeric"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Height") - .HasColumnType("numeric"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Price") - .HasColumnType("numeric"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.Property("Weight") - .HasColumnType("numeric"); - - b.Property("Width") - .HasColumnType("numeric"); - - b.HasKey("Id"); - - b.ToTable("Products"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Service", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContextId") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ContextId"); - - b.ToTable("Services"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.Announce", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("For") - .HasColumnType("smallint"); - - b.Property("Message") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Sender") - .HasColumnType("text"); - - b.Property("Topic") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("Announce"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.DismissClicked", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("NotificationId") - .HasColumnType("bigint"); - - b.HasKey("UserId", "NotificationId"); - - b.HasIndex("NotificationId"); - - b.ToTable("DismissClicked"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Instrument", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.HasKey("Id"); - - b.ToTable("Instrument"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.InstrumentRating", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("InstrumentId") - .HasColumnType("bigint"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasAlternateKey("InstrumentId", "OwnerId"); - - b.HasIndex("OwnerId"); - - b.ToTable("InstrumentRating"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalPreference", b => - { - b.Property("OwnerProfileId") - .HasColumnType("text"); - - b.Property("DjSettingsUserId") - .HasColumnType("text"); - - b.Property("MusicLoverSettingsUserId") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("TendencyId") - .HasColumnType("bigint"); - - b.HasKey("OwnerProfileId"); - - b.HasIndex("DjSettingsUserId"); - - b.HasIndex("MusicLoverSettingsUserId"); - - b.HasIndex("TendencyId"); - - b.ToTable("MusicalPreference"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalTendency", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.HasKey("Id"); - - b.ToTable("MusicalTendency"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.DjSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("SoundCloudId") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("DjSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.Instrumentation", b => - { - b.Property("InstrumentId") - .HasColumnType("bigint"); - - b.Property("UserId") - .HasColumnType("text"); - - b.HasKey("InstrumentId", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("Instrumentation"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.MusicLoverSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("MusicLoverSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.Property("CreationToken") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ExecutorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("OrderReference") - .HasColumnType("text"); - - b.Property("PaypalPayerId") - .HasColumnType("text"); - - b.Property("State") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("CreationToken"); - - b.HasIndex("ExecutorId"); - - b.ToTable("PayPalPayment"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApplicationUserId") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("Circle"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.CircleMember", b => - { - b.Property("MemberId") - .HasColumnType("text"); - - b.Property("CircleId") - .HasColumnType("bigint"); - - b.HasKey("MemberId", "CircleId"); - - b.HasIndex("CircleId"); - - b.ToTable("CircleMembers"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Contact", b => - { - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("AddressId") - .HasColumnType("bigint"); - - b.Property("ApplicationUserId") - .HasColumnType("text"); - - b.Property("EMail") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.HasKey("OwnerId", "UserId"); - - b.HasIndex("AddressId"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("Contact"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.HyperLink", b => - { - b.Property("HRef") - .HasColumnType("text"); - - b.Property("Method") - .HasColumnType("text"); - - b.Property("BrusherProfileUserId") - .HasColumnType("text"); - - b.Property("ContentType") - .HasColumnType("text"); - - b.Property("PayPalPaymentCreationToken") - .HasColumnType("text"); - - b.Property("Rel") - .HasColumnType("text"); - - b.HasKey("HRef", "Method"); - - b.HasIndex("BrusherProfileUserId"); - - b.HasIndex("PayPalPaymentCreationToken"); - - b.ToTable("HyperLink"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Location", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Address") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Latitude") - .HasColumnType("double precision"); - - b.Property("Longitude") - .HasColumnType("double precision"); - - b.HasKey("Id"); - - b.ToTable("Locations"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.PostalAddress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("City") - .HasColumnType("text"); - - b.Property("Country") - .HasColumnType("text"); - - b.Property("PostalCode") - .HasColumnType("text"); - - b.Property("Province") - .HasColumnType("text"); - - b.Property("State") - .HasColumnType("text"); - - b.Property("Street1") - .HasColumnType("text"); - - b.Property("Street2") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Yavsc.Models.Skill", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("SiteSkills"); - }); - - modelBuilder.Entity("Yavsc.Models.Streaming.LiveFlow", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DifferedFileName") - .HasColumnType("text"); - - b.Property("MediaType") - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Pitch") - .HasColumnType("text"); - - b.Property("SequenceNumber") - .HasColumnType("integer"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("LiveFlow"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.Property("Code") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Hidden") - .HasColumnType("boolean"); - - b.Property("Moderated") - .HasColumnType("boolean"); - - b.Property("ModeratorGroupName") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentCode") - .HasColumnType("text"); - - b.Property("Photo") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("SettingsClassName") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Code"); - - b.HasIndex("ParentCode"); - - b.ToTable("Activities"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CoWorking", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FormationSettingsUserId") - .HasColumnType("text"); - - b.Property("PerformerId") - .HasColumnType("text"); - - b.Property("WorkingForId") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("FormationSettingsUserId"); - - b.HasIndex("PerformerId"); - - b.HasIndex("WorkingForId"); - - b.ToTable("CoWorking"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CommandForm", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ActionName") - .HasColumnType("text"); - - b.Property("ActivityCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ActivityCode"); - - b.ToTable("CommandForm"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Country", b => - { - b.Property("Code") - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Code"); - - b.ToTable("Countries"); - - b.HasData( - new - { - Code = "fr", - DisplayName = "France" - }, - new - { - Code = "en", - DisplayName = "England" - }, - new - { - Code = "pt", - DisplayName = "Portugal" - }); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DomaineActiviteCode") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("Langue") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Nom") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("DomaineActiviteCode", "Langue", "Nom") - .IsUnique(); - - b.ToTable("DictionnaireMetier"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerCodeInputValidation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CountryCode") - .IsRequired() - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("ErrorMessage") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("RegularExpression") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CountryCode"); - - b.ToTable("PerformerCodeInputValidations"); - - b.HasData( - new - { - Id = 1L, - CountryCode = "fr", - ErrorMessage = "Le code FR doit contenir entre 9 et 14 chiffres.", - RegularExpression = "^[0-9]{9,14}$" - }, - new - { - Id = 2L, - CountryCode = "en", - ErrorMessage = "Le code EN doit contenir entre 8 et 14 caracteres alphanumeriques.", - RegularExpression = "^[A-Za-z0-9]{8,14}$" - }, - new - { - Id = 3L, - CountryCode = "pt", - ErrorMessage = "Le code PT doit contenir exactement 9 chiffres.", - RegularExpression = "^[0-9]{9}$" - }); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.Property("PerformerId") - .HasColumnType("text"); - - b.Property("AcceptNotifications") - .HasColumnType("boolean"); - - b.Property("AcceptPublicContact") - .HasColumnType("boolean"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("ExerciseCountryCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("MaxDailyCost") - .HasColumnType("integer"); - - b.Property("MinDailyCost") - .HasColumnType("integer"); - - b.Property("OrganizationAddressId") - .HasColumnType("bigint"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("SIREN") - .IsRequired() - .HasColumnType("text"); - - b.Property("UseGeoLocalizationToReduceDistanceWithClients") - .HasColumnType("boolean"); - - b.Property("WebSite") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("PerformerId"); - - b.HasIndex("OrganizationAddressId"); - - b.ToTable("Performers"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Profiles.FormationSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("FormationSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.TermeMetier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DateSoumission") - .HasColumnType("timestamp with time zone"); - - b.Property("DateValidation") - .HasColumnType("timestamp with time zone"); - - b.Property("Definition") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("DictionnaireMetierId") - .HasColumnType("bigint"); - - b.Property("Langue") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Mot") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ProposeParId") - .HasMaxLength(450) - .HasColumnType("character varying(450)"); - - b.Property("StatutValidation") - .HasColumnType("integer"); - - b.Property("ValideParId") - .HasMaxLength(450) - .HasColumnType("character varying(450)"); - - b.HasKey("Id"); - - b.HasIndex("ProposeParId"); - - b.HasIndex("ValideParId"); - - b.HasIndex("DictionnaireMetierId", "Langue", "Mot") - .IsUnique(); - - b.ToTable("TermeMetier"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.UserActivity", b => - { - b.Property("DoesCode") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("DoesCode", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("UserActivities"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.Calendar.Period", b => - { - b.Property("Start") - .HasColumnType("timestamp with time zone"); - - b.Property("End") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Start", "End"); - - b.ToTable("Period"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.EMailing.MailingTemplate", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Body") - .HasMaxLength(65536) - .HasColumnType("character varying(65536)"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ReplyToAddress") - .HasColumnType("text"); - - b.Property("ToSend") - .HasColumnType("integer"); - - b.Property("Topic") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("MailingTemplate"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.ProjectBuildConfiguration", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ProjectId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ProjectId"); - - b.ToTable("ProjectBuildConfiguration"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Branch") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text"); - - b.Property("Url") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("GitRepositoryReference"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairCutQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("AdditionalInfo") - .IsRequired() - .HasColumnType("text"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.Property("SelectedProfileUserId") - .HasColumnType("text"); - - b.HasIndex("LocationId"); - - b.HasIndex("PrestationId"); - - b.HasIndex("SelectedProfileUserId"); - - b.HasDiscriminator().HasValue("HairCutQuery"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.HasIndex("LocationId"); - - b.ToTable("NominativeServiceCommands", t => - { - t.Property("EventDate") - .HasColumnName("HairMultiCutQuery_EventDate"); - - t.Property("LocationId") - .HasColumnName("HairMultiCutQuery_LocationId"); - }); - - b.HasDiscriminator().HasValue("HairMultiCutQuery"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.RdvQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.Property("LocationType") - .HasColumnType("integer"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.HasIndex("LocationId"); - - b.ToTable("NominativeServiceCommands", t => - { - t.Property("EventDate") - .HasColumnName("RdvQuery_EventDate"); - - t.Property("LocationId") - .HasColumnName("RdvQuery_LocationId"); - }); - - b.HasDiscriminator().HasValue("RdvQuery"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("GitId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Version") - .HasColumnType("text"); - - b.HasIndex("GitId"); - - b.HasDiscriminator().HasValue("Project"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("UserClaims") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Properties") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Scopes") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Secrets") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", null) - .WithMany("UserClaims") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") - .WithMany() - .HasForeignKey("ScopeId1"); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", null) - .WithMany("Properties") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") - .WithMany() - .HasForeignKey("ScopeId1"); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("Claims") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("AllowedCorsOrigins") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedGrantTypes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("IdentityProviderRestrictions") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("PostLogoutRedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("Properties") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("RedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedScopes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("ClientSecrets") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("UserClaims") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("Properties") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Yavsc.Models.Access.Ban", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "TargetUser") - .WithMany() - .HasForeignKey("TargetId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TargetUser"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.BlackListed", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("BlackList") - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToBlogPost", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Target") - .WithMany("ACL") - .HasForeignKey("BlogPostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Relationship.Circle", "Allowed") - .WithMany() - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Allowed"); - - b.Navigation("Target"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToFile", b => - { - b.HasOne("Yavsc.Models.Relationship.Circle", "Allowed") - .WithMany() - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Allowed"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.AccountBalance", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithOne("AccountBalance") - .HasForeignKey("Yavsc.Models.AccountBalance", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "PostalAddress") - .WithMany() - .HasForeignKey("PostalAddressId"); - - b.Navigation("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.BalanceImpact", b => - { - b.HasOne("Yavsc.Models.AccountBalance", "Balance") - .WithMany() - .HasForeignKey("BalanceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Balance"); - }); - - modelBuilder.Entity("Yavsc.Models.Bank.BankIdentity", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany("BankInfo") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.CommandLine", b => - { - b.HasOne("Yavsc.Models.Billing.Estimate", null) - .WithMany("Bill") - .HasForeignKey("EstimateId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Billing.EstimateTemplate", null) - .WithMany("Bill") - .HasForeignKey("EstimateTemplateId"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Billing.NominativeServiceCommand", "Query") - .WithMany() - .HasForeignKey("CommandId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Owner"); - - b.Navigation("Query"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.NominativeServiceCommand", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany() - .HasForeignKey("ActivityCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Payment.PayPalPayment", "Regularization") - .WithMany() - .HasForeignKey("PaymentId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "PerformerProfile") - .WithMany() - .HasForeignKey("PerformerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Context"); - - b.Navigation("PerformerProfile"); - - b.Navigation("Regularization"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Signature", b => - { - b.HasOne("Yavsc.Models.Billing.Estimate", "Estimate") - .WithMany("Signatures") - .HasForeignKey("EstimateId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Signer") - .WithMany() - .HasForeignKey("SignerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Estimate"); - - b.Navigation("Signer"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogAttachedFile", b => - { - b.HasOne("Yavsc.Models.Blog.UploadedFile", "File") - .WithMany() - .HasForeignKey("FileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("File"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Author") - .WithMany("Posts") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Author"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogTag", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany("Tags") - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Relationship.Tag", "Tag") - .WithMany() - .HasForeignKey("TagId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - - b.Navigation("Tag"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Author") - .WithMany("BlogComments") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Yavsc.Models.Blog.Comment", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId"); - - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany("Comments") - .HasForeignKey("ReceiverId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - - b.Navigation("Parent"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithOne("Publication") - .HasForeignKey("Yavsc.Models.BlogSpotPublication", "PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.ScheduledEvent", b => - { - b.HasOne("Yavsc.Models.Calendar.Schedule", null) - .WithMany("Events") - .HasForeignKey("ScheduleOwnerId"); - - b.HasOne("Yavsc.Server.Models.Calendar.Period", "Period") - .WithMany() - .HasForeignKey("PeriodStart", "PeriodEnd"); - - b.Navigation("Period"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatConnection", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("Connections") - .HasForeignKey("ApplicationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("Rooms") - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoomAccess", b => - { - b.HasOne("Yavsc.Models.Chat.ChatRoom", "Room") - .WithMany("Moderation") - .HasForeignKey("ChannelName") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany("RoomAccess") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Room"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.HasOne("Yavsc.Models.Calendar.Schedule", "Schedule") - .WithMany() - .HasForeignKey("ScheduleOwnerId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "BaseProfile") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BaseProfile"); - - b.Navigation("Schedule"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestationCollectionItem", b => - { - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany() - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.HairMultiCutQuery", "Query") - .WithMany("Prestations") - .HasForeignKey("QueryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Prestation"); - - b.Navigation("Query"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaint", b => - { - b.HasOne("Yavsc.Models.Drawing.Color", "Color") - .WithMany() - .HasForeignKey("ColorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Color"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaintInstance", b => - { - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany("Taints") - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.HairTaint", "Taint") - .WithMany() - .HasForeignKey("TaintId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Prestation"); - - b.Navigation("Taint"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Fixing.Bug", b => - { - b.HasOne("Yavsc.Models.IT.Evolution.Feature", "False") - .WithMany() - .HasForeignKey("FeatureId"); - - b.Navigation("False"); - }); - - modelBuilder.Entity("Yavsc.Models.Identity.DeviceDeclaration", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "DeviceOwner") - .WithMany("DeviceDeclaration") - .HasForeignKey("DeviceOwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("DeviceOwner"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.DeclarationFlag", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustDeclaration", "Declaration") - .WithMany("Flags") - .HasForeignKey("DeclarationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Kyc.RegexAlertPattern", "Pattern") - .WithMany() - .HasForeignKey("PatternId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Declaration"); - - b.Navigation("Pattern"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.ModerationLog", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustDeclaration", "Declaration") - .WithMany() - .HasForeignKey("DeclarationId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Declaration"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustToken", "Subject") - .WithMany("Declarations") - .HasForeignKey("TrustTokenId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Subject"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Service", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany("Services") - .HasForeignKey("ContextId"); - - b.Navigation("Context"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.Announce", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.DismissClicked", b => - { - b.HasOne("Yavsc.Abstract.Models.Messaging.Notification", "Notified") - .WithMany() - .HasForeignKey("NotificationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Notified"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.InstrumentRating", b => - { - b.HasOne("Yavsc.Models.Musical.Instrument", "Instrument") - .WithMany() - .HasForeignKey("InstrumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Profile") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Instrument"); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalPreference", b => - { - b.HasOne("Yavsc.Models.Musical.Profiles.DjSettings", null) - .WithMany("SoundColor") - .HasForeignKey("DjSettingsUserId"); - - b.HasOne("Yavsc.Models.Musical.Profiles.MusicLoverSettings", null) - .WithMany("SoundColor") - .HasForeignKey("MusicLoverSettingsUserId"); - - b.HasOne("Yavsc.Models.Musical.MusicalTendency", "MusicalTendency") - .WithMany() - .HasForeignKey("TendencyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MusicalTendency"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.Instrumentation", b => - { - b.HasOne("Yavsc.Models.Musical.Instrument", "Tool") - .WithMany() - .HasForeignKey("InstrumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Tool"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Executor") - .WithMany() - .HasForeignKey("ExecutorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Executor"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany("Circles") - .HasForeignKey("ApplicationUserId"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.CircleMember", b => - { - b.HasOne("Yavsc.Models.Relationship.Circle", "Circle") - .WithMany("Members") - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Member") - .WithMany("Membership") - .HasForeignKey("MemberId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Circle"); - - b.Navigation("Member"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Contact", b => - { - b.HasOne("Yavsc.Models.Relationship.PostalAddress", "PostalAddress") - .WithMany() - .HasForeignKey("AddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany("Book") - .HasForeignKey("ApplicationUserId"); - - b.Navigation("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.HyperLink", b => - { - b.HasOne("Yavsc.Models.Haircut.BrusherProfile", null) - .WithMany("Links") - .HasForeignKey("BrusherProfileUserId"); - - b.HasOne("Yavsc.Models.Payment.PayPalPayment", null) - .WithMany("Links") - .HasForeignKey("PayPalPaymentCreationToken"); - }); - - modelBuilder.Entity("Yavsc.Models.Streaming.LiveFlow", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Parent") - .WithMany("Children") - .HasForeignKey("ParentCode"); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CoWorking", b => - { - b.HasOne("Yavsc.Models.Workflow.Profiles.FormationSettings", null) - .WithMany("CoWorking") - .HasForeignKey("FormationSettingsUserId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Performer") - .WithMany() - .HasForeignKey("PerformerId"); - - b.HasOne("Yavsc.Models.ApplicationUser", "WorkingFor") - .WithMany() - .HasForeignKey("WorkingForId"); - - b.Navigation("Performer"); - - b.Navigation("WorkingFor"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CommandForm", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany("Forms") - .HasForeignKey("ActivityCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Context"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "DomaineActivite") - .WithMany() - .HasForeignKey("DomaineActiviteCode") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("DomaineActivite"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerCodeInputValidation", b => - { - b.HasOne("Yavsc.Models.Workflow.Country", "Country") - .WithMany() - .HasForeignKey("CountryCode") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Country"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "OrganizationAddress") - .WithMany() - .HasForeignKey("OrganizationAddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Performer") - .WithMany() - .HasForeignKey("PerformerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("OrganizationAddress"); - - b.Navigation("Performer"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.TermeMetier", b => - { - b.HasOne("Yavsc.Models.Workflow.DictionnaireMetier", "DictionnaireMetier") - .WithMany("Termes") - .HasForeignKey("DictionnaireMetierId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "ProposePar") - .WithMany() - .HasForeignKey("ProposeParId"); - - b.HasOne("Yavsc.Models.ApplicationUser", "ValidePar") - .WithMany() - .HasForeignKey("ValideParId"); - - b.Navigation("DictionnaireMetier"); - - b.Navigation("ProposePar"); - - b.Navigation("ValidePar"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.UserActivity", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Does") - .WithMany() - .HasForeignKey("DoesCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "User") - .WithMany("Activity") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Does"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.ProjectBuildConfiguration", b => - { - b.HasOne("Yavsc.Server.Models.IT.Project", "TargetProject") - .WithMany("Configurations") - .HasForeignKey("ProjectId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TargetProject"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairCutQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId"); - - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany() - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.BrusherProfile", "SelectedProfile") - .WithMany() - .HasForeignKey("SelectedProfileUserId"); - - b.Navigation("Location"); - - b.Navigation("Prestation"); - - b.Navigation("SelectedProfile"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.RdvQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.HasOne("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", "Repository") - .WithMany() - .HasForeignKey("GitId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Repository"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => - { - b.Navigation("Properties"); - - b.Navigation("Scopes"); - - b.Navigation("Secrets"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => - { - b.Navigation("AllowedCorsOrigins"); - - b.Navigation("AllowedGrantTypes"); - - b.Navigation("AllowedScopes"); - - b.Navigation("Claims"); - - b.Navigation("ClientSecrets"); - - b.Navigation("IdentityProviderRestrictions"); - - b.Navigation("PostLogoutRedirectUris"); - - b.Navigation("Properties"); - - b.Navigation("RedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.Navigation("AccountBalance"); - - b.Navigation("BankInfo"); - - b.Navigation("BlackList"); - - b.Navigation("BlogComments"); - - b.Navigation("Book"); - - b.Navigation("Circles"); - - b.Navigation("Connections"); - - b.Navigation("DeviceDeclaration"); - - b.Navigation("Membership"); - - b.Navigation("Posts"); - - b.Navigation("RoomAccess"); - - b.Navigation("Rooms"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.Navigation("Bill"); - - b.Navigation("Signatures"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.EstimateTemplate", b => - { - b.Navigation("Bill"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.Navigation("ACL"); - - b.Navigation("Comments"); - - b.Navigation("Publication") - .IsRequired(); - - b.Navigation("Tags"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.Navigation("Events"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.Navigation("Moderation"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.Navigation("Links"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestation", b => - { - b.Navigation("Taints"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.Navigation("Flags"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustToken", b => - { - b.Navigation("Declarations"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.DjSettings", b => - { - b.Navigation("SoundColor"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.MusicLoverSettings", b => - { - b.Navigation("SoundColor"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.Navigation("Links"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.Navigation("Members"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.Navigation("Children"); - - b.Navigation("Forms"); - - b.Navigation("Services"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.Navigation("Termes"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.Navigation("Activity"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Profiles.FormationSettings", b => - { - b.Navigation("CoWorking"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.Navigation("Prestations"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.Navigation("Configurations"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.cs b/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.cs deleted file mode 100644 index d29c153cb..000000000 --- a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Yavsc.Migrations -{ - /// - public partial class DropSignatureFilePath : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "FilePath", - table: "Signatures"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "FilePath", - table: "Signatures", - type: "text", - nullable: false, - defaultValue: ""); - } - } -} diff --git a/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs index 2b8b98354..2ea15651e 100644 --- a/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace Yavsc.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.12") + .HasAnnotation("ProductVersion", "10.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -1521,6 +1521,10 @@ namespace Yavsc.Migrations b.Property("EstimateId") .HasColumnType("bigint"); + b.Property("FilePath") + .IsRequired() + .HasColumnType("text"); + b.Property("SignerId") .IsRequired() .HasColumnType("text"); @@ -3955,8 +3959,8 @@ namespace Yavsc.Migrations modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => { b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithOne("Publication") - .HasForeignKey("Yavsc.Models.BlogSpotPublication", "PostId") + .WithMany() + .HasForeignKey("PostId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); @@ -4610,9 +4614,6 @@ namespace Yavsc.Migrations b.Navigation("Comments"); - b.Navigation("Publication") - .IsRequired(); - b.Navigation("Tags"); }); diff --git a/src/Yavsc.Org/Views/Account/Delete.cshtml b/src/Yavsc.Org/Views/Account/Delete.cshtml deleted file mode 100644 index 7443d3d2a..000000000 --- a/src/Yavsc.Org/Views/Account/Delete.cshtml +++ /dev/null @@ -1,21 +0,0 @@ -@model UnregisterViewModel -@{ - ViewBag.Title = "Supprimer mon compte"; -} - -

@ViewBag.Title

- -
-
-

- Cette action est immédiate et irréversible. Toutes les données - associées à votre compte seront supprimées, et vous serez déconnecté. -

- -
- @Html.Hidden("UserId") - - Annuler -
-
-
\ No newline at end of file diff --git a/src/Yavsc.Server/Helpers/EstimateSignatureFileHelper.cs b/src/Yavsc.Server/Helpers/EstimateSignatureFileHelper.cs new file mode 100644 index 000000000..29062bc3c --- /dev/null +++ b/src/Yavsc.Server/Helpers/EstimateSignatureFileHelper.cs @@ -0,0 +1,158 @@ +#nullable enable annotations + +using System.Security.Claims; +using System.Text; +using System.Text.Json; +using Microsoft.AspNetCore.Http; +using Yavsc.Models.Billing; +using Yavsc.Server.Models.FileSystem; +namespace Yavsc.Server.Helpers; + +/// +/// Filesystem counterpart of : writes +/// the wire-format JSON payload next to the user's other files, +/// under signatures/, and updates the user's disk quota. +/// +/// This is the JSON counterpart of the legacy +/// ReceiveProSignatureAsync method, which stored +/// sign-{billingCode}-{signType}-{estimateId}.png blobs. +/// We don't reuse that helper because (a) the wire format is no +/// longer a binary image, (b) there's no billingCode on +/// a freshly signed estimate in our model, and (c) the legacy +/// helper takes an whereas our pipeline +/// decodes a JSON body upstream of the controller and passes +/// in directly. +/// +public static class EstimateSignatureFileHelper +{ + /// + /// Sub-directory under the user's root where signature + /// payloads live. Kept short to leave room in PATH_MAX on + /// legacy filesystems; the rest of the filename is + /// sign-{type}-{estimateId}-{utcTicks}.json. + /// + public const string SignaturesSubdir = "signatures"; + + /// + /// Format string for signature file names. Public so the + /// migration and the admin tools can list by pattern. + /// + public static string FileNameFormat(SignatureType type, long estimateId, long utcTicks) + => $"sign-{type.ToString().ToLowerInvariant()}-{estimateId}-{utcTicks}.json"; + + /// + /// Persist a signature wire payload to disk. Returns the + /// file info (relative path under the user's root) suitable + /// for storing in ; the + /// caller is responsible for the database write. + /// + /// Signed-in user. Their + /// Identity.Name locates the disk root via + /// . + /// + /// Estimate this signature + /// attaches to. Used in the file name for human inspection + /// and to support multiple versions over time. + /// Provider or client signature. + /// Decoded wire payload (strokes + + /// coordinateMax + capturedAtUtc). Already validated + /// upstream. + /// Cancellation token forwarded to + /// the file write. + public static async Task ReceiveEstimateSignatureAsync( + this ClaimsPrincipal user, + long estimateId, + SignatureType type, + SignaturePadPayload payload, + SiteSettings siteSettings, + CancellationToken token = default) + { + if (user is null) throw new ArgumentNullException(nameof(user)); + if (payload is null) throw new ArgumentNullException(nameof(payload)); + if (estimateId <= 0) throw new ArgumentOutOfRangeException(nameof(estimateId)); + + // Ensure the user has a /signatures/ sub-directory we can + // write to. EnsureDestinationDirectory throws on invalid + // paths and creates the directory on the way; the + // SignaturesSubdir constant is a server-controlled value + // (not user-derived), so we skip the IsValidYavscPath + // check that ReceiveUserFile performs on user-supplied + // subpaths. + var root = user.EnsureDestinationDirectory(SignaturesSubdir, siteSettings); + + var fileName = FileNameFormat(type, estimateId, DateTime.UtcNow.Ticks); + var fullPath = Path.Combine(root, fileName); + + var envelope = new + { + format = "yavsc.signature/v1", + coordinateMax = payload.CoordinateMax, + capturedAtUtc = payload.CapturedAtUtc, + estimateId, + type = type.ToString(), + // Identity.Name is the username; we keep the wire + // payload keyed on the username rather than the + // numeric/guid Id so disk-side human inspection + // (e.g. cat sign-pro-1234-...json) is self-evident. + signerName = user.Identity?.Name, + strokes = payload.Strokes, + strokeCount = CountStrokes(payload.Strokes), + }; + + var json = JsonSerializer.Serialize(envelope, new JsonSerializerOptions { WriteIndented = true }); + await File.WriteAllTextAsync(fullPath, json, Encoding.UTF8, token).ConfigureAwait(false); + + // Quota update is the controller's responsibility: the + // helper has no DbContext access, and a ClaimsPrincipal + // is not an ApplicationUser. The controller looks up + // the user by Identity.Name and bumps DiskUsage after + // a successful database write. + + return new FileReceivedInfo(root, fileName); + } + + private static int CountStrokes(int[] strokes) + { + int n = 0; + for (int i = 0; i < strokes.Length;) + { + int k = strokes[i]; + if (k <= 0) break; + n++; + i += 1 + 2 * k; + } + return n; + } +} + +/// +/// Wire payload accepted by the signature endpoint and +/// persisted by . +/// Mirrors PostIt.Models.SignaturePadData's JSON shape +/// (without the disk-only envelope fields) so the two sides +/// stay trivially compatible. +/// +public class SignaturePadPayload +{ + /// + /// Normalised coordinate upper bound. Must be + /// PostIt.Models.SignaturePadData.CoordinateMax + /// (10_000) today; declared as a property so a future + /// resolution change can be replayed against the same + /// wire format. + /// + public int CoordinateMax { get; set; } = 10_000; + + /// + /// Client-reported capture time. The server may ignore + /// this for ordering (UTC now is the truth) but keeps it + /// for round-trip display. + /// + public DateTime CapturedAtUtc { get; set; } + + /// + /// Wire strokes. See + /// PostIt.Models.SignaturePadData for the format. + /// + public int[] Strokes { get; set; } = Array.Empty(); +} diff --git a/src/Yavsc.Server/Helpers/EstimateSignaturePersister.cs b/src/Yavsc.Server/Helpers/EstimateSignaturePersister.cs deleted file mode 100644 index be8848e11..000000000 --- a/src/Yavsc.Server/Helpers/EstimateSignaturePersister.cs +++ /dev/null @@ -1,111 +0,0 @@ -#nullable enable annotations - -using System.Security.Claims; -using Microsoft.EntityFrameworkCore; -using Yavsc.Models; -using Yavsc.Models.Billing; - -namespace Yavsc.Server.Helpers; - -/// -/// Shared signature-persistence logic for estimates, extracted from -/// BillingController.Sign. Stages the -/// row (find-or-add by (EstimateId, Type)) for the PostIt -/// wire-format payload — but does not call -/// SaveChanges. The caller owns the transaction so it can -/// combine the signature write with its own changes (e.g. setting a -/// validation date or a query status) in a single -/// SaveChanges. -/// -/// -/// The column is the source of -/// truth; there is no on-disk copy, so no disk-quota accounting is -/// involved. -/// -/// -public static class EstimateSignaturePersister -{ - /// - /// Stage a signature for signed by - /// as . Upserts - /// the row for the - /// (EstimateId, Type) pair, writing the wire payload - /// (Strokes, CoordinateMax, CapturedAtUtc) - /// and . Returns the staged - /// ; the caller persists with - /// SaveChanges. - /// - public static async Task StageAsync( - ApplicationDbContext db, - long estimateId, - SignatureType type, - string signerId, - SignaturePadPayload payload, - CancellationToken token = default) - { - if (db is null) throw new ArgumentNullException(nameof(db)); - if (payload is null) throw new ArgumentNullException(nameof(payload)); - if (estimateId <= 0) throw new ArgumentOutOfRangeException(nameof(estimateId)); - - // Find-or-add: the (EstimateId, Type) pair is unique, so a - // second submission for the same side of the estimate - // replaces the previous signature. EF translates this into a - // single UPDATE when the row exists and an INSERT otherwise; - // the unique index in ApplicationDbContext is the - // database-level guarantee that the contract holds if two - // requests race. - var signature = await db.Signatures - .FirstOrDefaultAsync(s => s.EstimateId == estimateId && s.Type == type, token) - .ConfigureAwait(false); - - if (signature is null) - { - signature = new Signature - { - EstimateId = estimateId, - SignerId = signerId, - Type = type, - }; - db.Signatures.Add(signature); - } - - signature.SignerId = signerId; - signature.CoordinateMax = payload.CoordinateMax; - signature.Strokes = payload.Strokes; - signature.CapturedAtUtc = payload.CapturedAtUtc; - - return signature; - } -} - -/// -/// Wire payload accepted by the signature path and staged by -/// . Mirrors -/// PostIt.Models.SignaturePadData's JSON shape (without the -/// disk-only envelope fields) so the two sides stay trivially -/// compatible. -/// -public class SignaturePadPayload -{ - /// - /// Normalised coordinate upper bound. Must be - /// PostIt.Models.SignaturePadData.CoordinateMax - /// (10_000) today; declared as a property so a future - /// resolution change can be replayed against the same - /// wire format. - /// - public int CoordinateMax { get; set; } = 10_000; - - /// - /// Client-reported capture time. The server may ignore - /// this for ordering (UTC now is the truth) but keeps it - /// for round-trip display. - /// - public DateTime CapturedAtUtc { get; set; } - - /// - /// Wire strokes. See - /// PostIt.Models.SignaturePadData for the format. - /// - public int[] Strokes { get; set; } = Array.Empty(); -} \ No newline at end of file diff --git a/src/Yavsc.Server/Models/Billing/Signature.cs b/src/Yavsc.Server/Models/Billing/Signature.cs index 1aac0cd0e..10da97f1b 100644 --- a/src/Yavsc.Server/Models/Billing/Signature.cs +++ b/src/Yavsc.Server/Models/Billing/Signature.cs @@ -7,8 +7,8 @@ namespace Yavsc.Models.Billing; /// /// One captured signature, attached to a single /// . Multiple versions are allowed per -/// (EstimateId, Type, SignerId) tuple — the controller reads the -/// most recent when asked. The wire-format payload is the +/// (EstimateId, Type, SignerId) tuple — the controller reads +/// the most recent when asked. The wire-format payload is the /// same int[] shape PostIt produces (see /// PostIt.Models.SignaturePadData): a length-prefixed /// sequence of strokes, each stroke being @@ -16,12 +16,13 @@ namespace Yavsc.Models.Billing; /// CoordinateMax]. /// /// -/// The column is the source of truth: the -/// signature is captured and stored entirely in-app (PostIt) and -/// round-tripped as JSON, with no on-disk copy. A dedicated table -/// (instead of a JSON column on ) keeps the -/// row narrow for list views and preserves -/// superseded versions for audit. +/// Why a separate table (instead of a JSON column on +/// ): the jalon 1 spec calls for at least +/// two distinct signatures per estimate cycle (provider +/// validation + client agreement) and the audit value of +/// preserving superseded versions. A dedicated table also keeps +/// the row narrow, which matters for +/// list views. /// /// public class Signature @@ -74,4 +75,16 @@ public class Signature public int[] Strokes { get; set; } = Array.Empty(); public DateTime CapturedAtUtc { get; set; } -} \ No newline at end of file + + /// + /// Path to the JSON-serialised wire payload on disk, + /// relative to UserFilesDirName. The disk copy is the + /// source of truth for the wire bytes; the + /// column is a denormalised index for queries. They are + /// written together in the same transaction by the + /// controller; the migration should keep them in sync + /// through ApplicationDbContext.SaveChanges. + /// + [Required] + public string FilePath { get; set; } +}