diff --git a/src/AssociationRegistry.Admin.Api/AssociationRegistry.Admin.Api.csproj b/src/AssociationRegistry.Admin.Api/AssociationRegistry.Admin.Api.csproj
index c891a455c..6fb893dc5 100755
--- a/src/AssociationRegistry.Admin.Api/AssociationRegistry.Admin.Api.csproj
+++ b/src/AssociationRegistry.Admin.Api/AssociationRegistry.Admin.Api.csproj
@@ -67,6 +67,7 @@
+
diff --git a/src/AssociationRegistry.Admin.Api/Infrastructure/Extensions/WolverineExtensions.cs b/src/AssociationRegistry.Admin.Api/Infrastructure/Extensions/WolverineExtensions.cs
index 19d7cfe7a..c0f052a48 100644
--- a/src/AssociationRegistry.Admin.Api/Infrastructure/Extensions/WolverineExtensions.cs
+++ b/src/AssociationRegistry.Admin.Api/Infrastructure/Extensions/WolverineExtensions.cs
@@ -6,16 +6,21 @@
using Grar.AddressMatch;
using Hosts.Configuration;
using JasperFx.CodeGeneration;
+using MessageHandling.Postgres.Dubbels;
+using Messages;
using Serilog;
using Vereniging;
using Wolverine;
using Wolverine.AmazonSqs;
using Wolverine.ErrorHandling;
+using Wolverine.Postgresql;
public static class WolverineExtensions
{
public static void AddWolverine(this WebApplicationBuilder builder)
{
+ const string AanvaardDubbeleVerenigingQueueName = "aanvaard-dubbele-vereniging-queue";
+
builder.Host.UseWolverine(
(context, options) =>
{
@@ -26,6 +31,8 @@ public static void AddWolverine(this WebApplicationBuilder builder)
options.Discovery.IncludeType();
options.Discovery.IncludeType();
options.Discovery.IncludeType();
+ options.Discovery.IncludeType();
+ options.Discovery.IncludeType();
options.OnException().RetryWithCooldown(
TimeSpan.FromSeconds(1),
@@ -50,12 +57,19 @@ public static void AddWolverine(this WebApplicationBuilder builder)
ConfigureAddressMatchPublisher(options, grarOptions.Sqs.AddressMatchQueueName);
- ConfiguredAddressMatchListener(options, grarOptions.Sqs.AddressMatchQueueName,
+ ConfigureAddressMatchListener(options, grarOptions.Sqs.AddressMatchQueueName,
grarOptions.Sqs.AddressMatchDeadLetterQueueName);
ConfigureGrarSyncListener(options, grarOptions.Sqs.GrarSyncQueueName, grarOptions.Sqs.GrarSyncDeadLetterQueueName,
grarOptions.Sqs.GrarSyncQueueListenerEnabled);
+ options.PersistMessagesWithPostgresql(context.Configuration.GetPostgreSqlOptionsSection().GetConnectionString());
+
+ options.PublishMessage()
+ .ToPostgresqlQueue(AanvaardDubbeleVerenigingQueueName);
+
+ options.ListenToPostgresqlQueue(AanvaardDubbeleVerenigingQueueName);
+
if (grarOptions.Wolverine.AutoProvision)
transportConfiguration.AutoProvision();
@@ -73,7 +87,7 @@ private static void ConfigureAddressMatchPublisher(WolverineOptions options, str
.MessageBatchSize(1);
}
- private static void ConfiguredAddressMatchListener(WolverineOptions options, string sqsQueueName, string sqsDeadLetterQueueName)
+ private static void ConfigureAddressMatchListener(WolverineOptions options, string sqsQueueName, string sqsDeadLetterQueueName)
{
options.ListenToSqsQueue(sqsQueueName, configure: configure =>
{
diff --git a/src/AssociationRegistry.Admin.Api/Infrastructure/ResponseWriter/ResponseWriter.cs b/src/AssociationRegistry.Admin.Api/Infrastructure/ResponseWriter/ResponseWriter.cs
index 80c1bf469..a06b78012 100644
--- a/src/AssociationRegistry.Admin.Api/Infrastructure/ResponseWriter/ResponseWriter.cs
+++ b/src/AssociationRegistry.Admin.Api/Infrastructure/ResponseWriter/ResponseWriter.cs
@@ -1,6 +1,6 @@
namespace AssociationRegistry.Admin.Api.Infrastructure.ResponseWriter;
-using AssociationRegistry.Admin.Api.Infrastructure.Extensions;
+using Extensions;
using Be.Vlaanderen.Basisregisters.Api.Exceptions;
using Microsoft.AspNetCore.Mvc;
diff --git a/src/AssociationRegistry.Admin.Api/Infrastructure/Sequence/SequenceGuarder.cs b/src/AssociationRegistry.Admin.Api/Infrastructure/Sequence/SequenceGuarder.cs
index ebfd31759..d9138ae08 100644
--- a/src/AssociationRegistry.Admin.Api/Infrastructure/Sequence/SequenceGuarder.cs
+++ b/src/AssociationRegistry.Admin.Api/Infrastructure/Sequence/SequenceGuarder.cs
@@ -1,8 +1,8 @@
namespace AssociationRegistry.Admin.Api.Infrastructure.Sequence;
-using AssociationRegistry.Admin.Api.Infrastructure.Extensions;
-using AssociationRegistry.Admin.Schema.Detail;
-using AssociationRegistry.EventStore;
+using Extensions;
+using Schema.Detail;
+using EventStore;
using Marten;
public class SequenceGuarder : ISequenceGuarder
diff --git a/src/AssociationRegistry.Admin.Api/Infrastructure/Validation/FluentValidatorExtensions.cs b/src/AssociationRegistry.Admin.Api/Infrastructure/Validation/FluentValidatorExtensions.cs
index 29b45122f..813e0902a 100644
--- a/src/AssociationRegistry.Admin.Api/Infrastructure/Validation/FluentValidatorExtensions.cs
+++ b/src/AssociationRegistry.Admin.Api/Infrastructure/Validation/FluentValidatorExtensions.cs
@@ -1,6 +1,6 @@
namespace AssociationRegistry.Admin.Api.Infrastructure.Validation;
-using AssociationRegistry.Admin.Api.Infrastructure.ExceptionHandlers;
+using ExceptionHandlers;
using FluentValidation;
using System.Diagnostics.CodeAnalysis;
diff --git a/src/AssociationRegistry.Admin.Api/MessageHandling/Postgres/Dubbels/AanvaardDubbeleVerenigingMessageHandler.cs b/src/AssociationRegistry.Admin.Api/MessageHandling/Postgres/Dubbels/AanvaardDubbeleVerenigingMessageHandler.cs
new file mode 100644
index 000000000..fe023d60e
--- /dev/null
+++ b/src/AssociationRegistry.Admin.Api/MessageHandling/Postgres/Dubbels/AanvaardDubbeleVerenigingMessageHandler.cs
@@ -0,0 +1,14 @@
+namespace AssociationRegistry.Admin.Api.MessageHandling.Postgres.Dubbels;
+
+using Messages;
+using Wolverine;
+
+public class AanvaardDubbeleVerenigingMessageHandler(IMessageBus messageBus)
+{
+ public async Task Handle(AanvaardDubbeleVerenigingMessage message, CancellationToken cancellationToken)
+ {
+ var command = message.ToCommand();
+
+ await messageBus.InvokeAsync(command, cancellationToken);
+ }
+}
diff --git a/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/BeheerVerenigingDetailMapper.cs b/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/BeheerVerenigingDetailMapper.cs
index b88914fa7..692e1e8c8 100644
--- a/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/BeheerVerenigingDetailMapper.cs
+++ b/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/BeheerVerenigingDetailMapper.cs
@@ -79,6 +79,7 @@ private static VerenigingDetail Map(
Relaties = vereniging.Relaties.Select(relatie => Map(relatie, baseUrl)).ToArray(),
Lidmaatschappen = vereniging.Lidmaatschappen.Select(lidmaatschap => Map(lidmaatschap, namenVoorLidmaatschapMapper)).ToArray(),
Bron = vereniging.Bron,
+ IsDubbelVan = vereniging.IsDubbelVan,
};
}
diff --git a/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/ResponseModels/VerenigingDetail.cs b/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/ResponseModels/VerenigingDetail.cs
index 3b153dc13..f7ece6314 100644
--- a/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/ResponseModels/VerenigingDetail.cs
+++ b/src/AssociationRegistry.Admin.Api/Verenigingen/Detail/ResponseModels/VerenigingDetail.cs
@@ -15,7 +15,7 @@ public class VerenigingDetail
// De unieke identificatie codes van de corresponderende verenigingen
[DataMember(Name = "CorresponderendeVCodes")]
- public string[] CorresponderendeVCodes { get; init; } = Array.Empty();
+ public string[] CorresponderendeVCodes { get; init; } = [];
/// Het type van deze vereniging
[DataMember(Name = "Verenigingstype")]
@@ -102,4 +102,8 @@ public class VerenigingDetail
///
[DataMember(Name = "Bron")]
public string Bron { get; set; } = null!;
+
+ /// De VCode van de vereniging waarvan deze vereniging een dubbel is
+ [DataMember(Name = "IsDubbelVan")]
+ public string IsDubbelVan { get; set; }
}
diff --git a/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/Examples/MarkeerAlsDubbelVanRequestExamples.cs b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/Examples/MarkeerAlsDubbelVanRequestExamples.cs
new file mode 100644
index 000000000..4879f3235
--- /dev/null
+++ b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/Examples/MarkeerAlsDubbelVanRequestExamples.cs
@@ -0,0 +1,13 @@
+namespace AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.Examples;
+
+using RequestModels;
+using Swashbuckle.AspNetCore.Filters;
+
+public class MarkeerAlsDubbelVanRequestExamples : IExamplesProvider
+{
+ public MarkeerAlsDubbelVanRequest GetExamples()
+ => new()
+ {
+ IsDubbelVan = "V0001002",
+ };
+}
diff --git a/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanController.cs b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanController.cs
new file mode 100644
index 000000000..add8310d0
--- /dev/null
+++ b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanController.cs
@@ -0,0 +1,87 @@
+namespace AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan;
+
+using Acties.MarkeerAlsDubbelVan;
+using Asp.Versioning;
+using Be.Vlaanderen.Basisregisters.Api;
+using Be.Vlaanderen.Basisregisters.Api.Exceptions;
+using Examples;
+using FluentValidation;
+using Framework;
+using Hosts.Configuration.ConfigurationBindings;
+using Infrastructure;
+using Infrastructure.Extensions;
+using Infrastructure.Middleware;
+using Infrastructure.Swagger.Annotations;
+using Infrastructure.Swagger.Examples;
+using Infrastructure.Validation;
+using Microsoft.AspNetCore.Mvc;
+using RequestModels;
+using Swashbuckle.AspNetCore.Filters;
+using Vereniging;
+using Wolverine;
+using ProblemDetails = Be.Vlaanderen.Basisregisters.BasicApiProblem.ProblemDetails;
+using ValidationProblemDetails = Be.Vlaanderen.Basisregisters.BasicApiProblem.ValidationProblemDetails;
+
+[ApiVersion("1.0")]
+[AdvertiseApiVersions("1.0")]
+[ApiRoute("verenigingen")]
+[SwaggerGroup.DecentraalBeheer]
+public class MarkeerAlsDubbelVanController : ApiController
+{
+ private readonly IMessageBus _messageBus;
+ private readonly IValidator _validator;
+ private readonly AppSettings _appSettings;
+
+ public MarkeerAlsDubbelVanController(IMessageBus messageBus, IValidator validator, AppSettings appSettings)
+ {
+ _messageBus = messageBus;
+ _validator = validator;
+ _appSettings = appSettings;
+ }
+
+ ///
+ /// Markeer een vereniging als dubbel van een andere vereniging.
+ ///
+ ///
+ /// Na het uitvoeren van deze actie wordt een sequentie teruggegeven via de `VR-Sequence` header.
+ /// Deze waarde kan gebruikt worden in andere endpoints om op te volgen of de aanpassing
+ /// al is doorgestroomd naar deze endpoints.
+ ///
+ /// De vCode van de vereniging.
+ /// De gegevens van de andere vereniging waarvan deze een dubbel is.
+ ///
+ /// If-Match header met ETag van de laatst gekende versie van de vereniging.
+ /// De vereniging werd als dubbel gemarkeerd.
+ /// Er was een probleem met de doorgestuurde waarden.
+ /// De gevraagde vereniging heeft niet de verwachte sequentiewaarde.
+ /// Er is een interne fout opgetreden.
+ [HttpPost("{vCode}/dubbelVan")]
+ [ConsumesJson]
+ [ProducesJson]
+ [SwaggerRequestExample(typeof(MarkeerAlsDubbelVanRequest), typeof(MarkeerAlsDubbelVanRequestExamples))]
+ [SwaggerResponseHeader(StatusCodes.Status202Accepted, WellknownHeaderNames.Sequence, type: "string",
+ description: "Het sequence nummer van deze request.")]
+ [SwaggerResponseHeader(StatusCodes.Status202Accepted, name: "ETag", type: "string",
+ description: "De versie van de vereniging die als dubbel werd gemarkeerd.")]
+ [SwaggerResponseExample(StatusCodes.Status400BadRequest, typeof(ProblemAndValidationProblemDetailsExamples))]
+ [SwaggerResponseExample(StatusCodes.Status412PreconditionFailed, typeof(PreconditionFailedProblemDetailsExamples))]
+ [SwaggerResponseExample(StatusCodes.Status500InternalServerError, typeof(InternalServerErrorResponseExamples))]
+ [ProducesResponseType(StatusCodes.Status202Accepted)]
+ [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status412PreconditionFailed)]
+ [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status500InternalServerError)]
+ public async Task Post(
+ [FromRoute] string vCode,
+ [FromBody] MarkeerAlsDubbelVanRequest request,
+ [FromServices] ICommandMetadataProvider metadataProvider,
+ [FromHeader(Name = "If-Match")] string? ifMatch = null)
+ {
+ await _validator.NullValidateAndThrowAsync(request);
+
+ var metaData = metadataProvider.GetMetadata(IfMatchParser.ParseIfMatch(ifMatch));
+ var envelope = new CommandEnvelope(request.ToCommand(vCode), metaData);
+ var commandResult = await _messageBus.InvokeAsync(envelope);
+
+ return this.AcceptedCommand(_appSettings, commandResult);
+ }
+}
diff --git a/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanValidator.cs b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanValidator.cs
new file mode 100644
index 000000000..ca364cb3a
--- /dev/null
+++ b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanValidator.cs
@@ -0,0 +1,17 @@
+namespace AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan;
+
+using FluentValidation;
+using Infrastructure.Validation;
+using RequestModels;
+
+// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+public class MarkeerAlsDubbelVanValidator : AbstractValidator
+{
+ public MarkeerAlsDubbelVanValidator()
+ {
+ RuleFor(r => r.IsDubbelVan)
+ .NotNull()
+ .NotEmpty()
+ .WithVeldIsVerplichtMessage(nameof(MarkeerAlsDubbelVanRequest.IsDubbelVan));
+ }
+}
diff --git a/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/RequestModels/MarkeerAlsDubbelVanRequest.cs b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/RequestModels/MarkeerAlsDubbelVanRequest.cs
new file mode 100644
index 000000000..bd2e8c6e1
--- /dev/null
+++ b/src/AssociationRegistry.Admin.Api/Verenigingen/Dubbels/FeitelijkeVereniging/MarkeerAlsDubbelVan/RequestModels/MarkeerAlsDubbelVanRequest.cs
@@ -0,0 +1,16 @@
+namespace AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.RequestModels;
+
+using Acties.MarkeerAlsDubbelVan;
+using System.Runtime.Serialization;
+using Vereniging;
+
+[DataContract]
+public class MarkeerAlsDubbelVanRequest
+{
+ /// De VCode van de vereniging waarvan deze vereniging een dubbel is.
+ [DataMember(Name = "isDubbelVan")]
+ public string IsDubbelVan { get; set; } = null!;
+
+ public MarkeerAlsDubbelVanCommand ToCommand(string vCode)
+ => new(VCode.Create(vCode), VCode.Create(IsDubbelVan));
+}
diff --git a/src/AssociationRegistry.Admin.Api/Verenigingen/Registreer/MetRechtspersoonlijkheid/RequestModels/RegistreerVerenigingUitKboRequestValidator.cs b/src/AssociationRegistry.Admin.Api/Verenigingen/Registreer/MetRechtspersoonlijkheid/RequestModels/RegistreerVerenigingUitKboRequestValidator.cs
index a8a231643..39d7c165c 100644
--- a/src/AssociationRegistry.Admin.Api/Verenigingen/Registreer/MetRechtspersoonlijkheid/RequestModels/RegistreerVerenigingUitKboRequestValidator.cs
+++ b/src/AssociationRegistry.Admin.Api/Verenigingen/Registreer/MetRechtspersoonlijkheid/RequestModels/RegistreerVerenigingUitKboRequestValidator.cs
@@ -1,7 +1,7 @@
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
namespace AssociationRegistry.Admin.Api.Verenigingen.Registreer.MetRechtspersoonlijkheid.RequestModels;
-using AssociationRegistry.Admin.Api.Infrastructure.Validation;
+using Infrastructure.Validation;
using FluentValidation;
public class RegistreerVerenigingUitKboRequestValidator : AbstractValidator
diff --git a/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjection.cs b/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjection.cs
index c3b1c0fe5..13abd21b3 100644
--- a/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjection.cs
+++ b/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjection.cs
@@ -224,6 +224,12 @@ public async Task Project(IEvent @event, IDocumentOpe
public async Task Project(IEvent @event, IDocumentOperations ops)
=> await Update(@event, ops, BeheerVerenigingDetailProjector.Apply);
+ public async Task Project(IEvent @event, IDocumentOperations ops)
+ => await Update(@event, ops, BeheerVerenigingDetailProjector.Apply);
+
+ public async Task Project(IEvent @event, IDocumentOperations ops)
+ => await Update(@event, ops, BeheerVerenigingDetailProjector.Apply);
+
private async Task SoftDelete(string? streamKey, IDocumentOperations ops)
=> ops.Delete(streamKey);
diff --git a/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjector.cs b/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjector.cs
index 1d88cff20..fd72239f5 100644
--- a/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjector.cs
+++ b/src/AssociationRegistry.Admin.ProjectionHost/Projections/Detail/BeheerVerenigingDetailProjector.cs
@@ -36,6 +36,7 @@ public static BeheerVerenigingDetailDocument Create(IEvent BeheerVerenigingDetailMapper.MapContactgegeven(
c, feitelijkeVerenigingWerdGeregistreerd.Data.Bron,
@@ -90,6 +91,7 @@ public static BeheerVerenigingDetailDocument Create(
DatumLaatsteAanpassing = verenigingMetRechtspersoonlijkheidWerdGeregistreerd.GetHeaderInstant(MetadataHeaderNames.Tijdstip).FormatAsBelgianDate(),
Status = VerenigingStatus.Actief,
IsUitgeschrevenUitPubliekeDatastroom = false,
+ IsDubbelVan = "",
Contactgegevens = [],
Locaties = [],
Vertegenwoordigers = [],
@@ -746,4 +748,19 @@ public static void Apply(IEvent lidmaatschapWerdToeg
.OrderBy(l => l.LidmaatschapId)
.ToArray();
}
+
+
+ public static void Apply(IEvent verenigingWerdGemarkeerdAlsDubbel, BeheerVerenigingDetailDocument document)
+ {
+ document.Status = VerenigingStatus.Dubbel;
+ document.IsDubbelVan = verenigingWerdGemarkeerdAlsDubbel.Data.VCodeAuthentiekeVereniging;
+ }
+
+ public static void Apply(IEvent verenigingAanvaardeDubbeleVereniging, BeheerVerenigingDetailDocument document)
+ {
+ document.CorresponderendeVCodes =
+ document.CorresponderendeVCodes
+ .Append(verenigingAanvaardeDubbeleVereniging.Data.VCodeDubbeleVereniging)
+ .ToArray();
+ }
}
diff --git a/src/AssociationRegistry.Admin.Schema/Constants/VerenigingStatus.cs b/src/AssociationRegistry.Admin.Schema/Constants/VerenigingStatus.cs
index 641ca5f3b..82dac9614 100644
--- a/src/AssociationRegistry.Admin.Schema/Constants/VerenigingStatus.cs
+++ b/src/AssociationRegistry.Admin.Schema/Constants/VerenigingStatus.cs
@@ -4,4 +4,5 @@ public static class VerenigingStatus
{
public const string Actief = "Actief";
public const string Gestopt = "Gestopt";
+ public const string Dubbel = "Dubbel";
}
diff --git a/src/AssociationRegistry.Admin.Schema/Detail/BeheerVerenigingDetailDocument.cs b/src/AssociationRegistry.Admin.Schema/Detail/BeheerVerenigingDetailDocument.cs
index dbcfce69e..0dc1655fe 100644
--- a/src/AssociationRegistry.Admin.Schema/Detail/BeheerVerenigingDetailDocument.cs
+++ b/src/AssociationRegistry.Admin.Schema/Detail/BeheerVerenigingDetailDocument.cs
@@ -38,4 +38,5 @@ public record BeheerVerenigingDetailDocument : IVCode, ISoftDeleted, IMetadata
[Identity] public string VCode { get; init; } = null!;
public bool Deleted { get; set; }
public DateTimeOffset? DeletedAt { get; set; }
+ public string IsDubbelVan { get; set; } = string.Empty;
}
diff --git a/src/AssociationRegistry.Public.Api/Infrastructure/Extensions/IQueryableExtensions.cs b/src/AssociationRegistry.Public.Api/Infrastructure/Extensions/IQueryableExtensions.cs
index c5bf48df3..4a0ae5a87 100644
--- a/src/AssociationRegistry.Public.Api/Infrastructure/Extensions/IQueryableExtensions.cs
+++ b/src/AssociationRegistry.Public.Api/Infrastructure/Extensions/IQueryableExtensions.cs
@@ -13,8 +13,8 @@ public static class IQueryableExtensions
public static IQueryable WithVCode(this IQueryable source, string vCode) where T : IVCode
=> source.Where(x => x.VCode.Equals(vCode, StringComparison.CurrentCultureIgnoreCase));
- public static IQueryable OnlyActief(this IQueryable source)
- => source.Where(x => x.Status == VerenigingStatus.Actief);
+ public static IQueryable OnlyActiefOrDubbel(this IQueryable source)
+ => source.Where(x => x.Status == VerenigingStatus.Actief || x.Status == VerenigingStatus.Dubbel);
public static IQueryable OnlyIngeschrevenInPubliekeDatastroom(this IQueryable source)
where T : ICanBeUitgeschrevenUitPubliekeDatastroom
diff --git a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/DetailVerenigingenController.cs b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/DetailVerenigingenController.cs
index 05b60eb46..efdb2388c 100644
--- a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/DetailVerenigingenController.cs
+++ b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/DetailVerenigingenController.cs
@@ -68,6 +68,6 @@ public async Task Detail(
.Query()
.WithVCode(vCode)
.OnlyIngeschrevenInPubliekeDatastroom()
- .OnlyActief()
+ .OnlyActiefOrDubbel()
.SingleOrDefaultAsync();
}
diff --git a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/PubliekVerenigingDetailMapper.cs b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/PubliekVerenigingDetailMapper.cs
index dc7c128a8..32146ff71 100644
--- a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/PubliekVerenigingDetailMapper.cs
+++ b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/PubliekVerenigingDetailMapper.cs
@@ -39,6 +39,8 @@ public static PubliekVerenigingDetailResponse Map(
Sleutels = document.Sleutels.Select(Map).ToArray(),
Relaties = document.Relaties.Select(r => Map(appSettings, r)).ToArray(),
Lidmaatschappen = document.Lidmaatschappen.Select(l => Map(l, lidmaatschapMapper)).ToArray(),
+ IsDubbelVan = document.IsDubbelVan,
+ CorresponderendeVCodes = document.CorresponderendeVCodes,
},
Metadata = new Metadata { DatumLaatsteAanpassing = document.DatumLaatsteAanpassing },
};
diff --git a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseExamples/DetailVerenigingResponseExamples.cs b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseExamples/DetailVerenigingResponseExamples.cs
index 7e49f8cd1..6a22fee1c 100644
--- a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseExamples/DetailVerenigingResponseExamples.cs
+++ b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseExamples/DetailVerenigingResponseExamples.cs
@@ -149,6 +149,8 @@ public PubliekVerenigingDetailResponse GetExamples()
AndereVereniging = "V0001111",
},
],
+ IsDubbelVan = "V0001002",
+ CorresponderendeVCodes = [],
},
Metadata = new Metadata { DatumLaatsteAanpassing = "2023-05-15" },
};
diff --git a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseModels/Vereniging.cs b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseModels/Vereniging.cs
index bd213cf89..dad80ca16 100644
--- a/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseModels/Vereniging.cs
+++ b/src/AssociationRegistry.Public.Api/Verenigingen/Detail/ResponseModels/Vereniging.cs
@@ -1,5 +1,6 @@
namespace AssociationRegistry.Public.Api.Verenigingen.Detail.ResponseModels;
+using AssociationRegistry.Vereniging;
using System.ComponentModel;
using System.Runtime.Serialization;
@@ -49,31 +50,37 @@ public class Vereniging
/// De contactgegevens van deze vereniging
[DataMember(Name = "Contactgegevens")]
- public Contactgegeven[] Contactgegevens { get; init; } = Array.Empty();
+ public Contactgegeven[] Contactgegevens { get; init; } = [];
/// Alle locaties waar deze vereniging actief is
[DataMember(Name = "Locaties")]
- public Locatie[] Locaties { get; init; } = Array.Empty();
+ public Locatie[] Locaties { get; init; } = [];
/// De hoofdactivititeiten van deze vereniging volgens het verenigingsloket
[DataMember(Name = "HoofdactiviteitenVerenigingsloket")]
- public HoofdactiviteitVerenigingsloket[] HoofdactiviteitenVerenigingsloket { get; init; } =
- Array.Empty();
+ public HoofdactiviteitVerenigingsloket[] HoofdactiviteitenVerenigingsloket { get; init; } = [];
/// De werkingsgebieden van deze vereniging
[DataMember(Name = "Werkingsgebieden")]
- public Werkingsgebied[] Werkingsgebieden { get; init; } =
- Array.Empty();
+ public Werkingsgebied[] Werkingsgebieden { get; init; } = [];
/// De sleutels die deze vereniging beheren
[DataMember(Name = "Sleutels")]
- public Sleutel[] Sleutels { get; init; } = Array.Empty();
+ public Sleutel[] Sleutels { get; init; } = [];
/// De relaties van deze vereniging
[DataMember(Name = "Relaties")]
- public Relatie[] Relaties { get; init; } = Array.Empty();
+ public Relatie[] Relaties { get; init; } = [];
/// De lidmaatschappen van deze vereniging
[DataMember(Name = "Lidmaatschappen")]
- public Lidmaatschap[] Lidmaatschappen { get; init; } = Array.Empty();
+ public Lidmaatschap[] Lidmaatschappen { get; init; } = [];
+
+ /// De VCode van de vereniging waarvan deze vereniging een dubbel is
+ [DataMember(Name = "IsDubbelVan")]
+ public string IsDubbelVan { get; set; }
+
+ // De unieke identificatie codes van de corresponderende verenigingen
+ [DataMember(Name = "CorresponderendeVCodes")]
+ public string[] CorresponderendeVCodes { get; init; } = [];
}
diff --git a/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjection.cs b/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjection.cs
index 3d50817d8..eab6b261e 100644
--- a/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjection.cs
+++ b/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjection.cs
@@ -211,6 +211,12 @@ public async Task Project(IEvent @event, IDocumentOpe
public async Task Project(IEvent @event, IDocumentOperations ops)
=> await Update(@event, ops, PubliekVerenigingDetailProjector.Apply);
+ public async Task Project(IEvent @event, IDocumentOperations ops)
+ => await Update(@event, ops, PubliekVerenigingDetailProjector.Apply);
+
+ public async Task Project(IEvent @event, IDocumentOperations ops)
+ => await Update(@event, ops, PubliekVerenigingDetailProjector.Apply);
+
private static async Task Update(
IEvent @event,
IDocumentOperations ops,
diff --git a/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjector.cs b/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjector.cs
index dfb29c517..3e90a1b06 100644
--- a/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjector.cs
+++ b/src/AssociationRegistry.Public.ProjectionHost/Projections/Detail/PubliekVerenigingDetailProjector.cs
@@ -79,6 +79,8 @@ public static PubliekVerenigingDetailDocument Create(
CodeerSysteem = CodeerSysteem.VR,
},
],
+ IsDubbelVan = "",
+ CorresponderendeVCodes = [],
};
public static PubliekVerenigingDetailDocument Create(
@@ -154,6 +156,8 @@ public static PubliekVerenigingDetailDocument Create(
CodeerSysteem = CodeerSysteem.KBO,
},
],
+ IsDubbelVan = "",
+ CorresponderendeVCodes = [],
};
private static PubliekVerenigingDetailDocument.HoofdactiviteitVerenigingsloket MapHoofdactiviteit(
@@ -775,4 +779,18 @@ public static void Apply(IEvent lidmaatschapWerdVerw
.OrderBy(l => l.LidmaatschapId)
.ToArray();
}
+
+ public static void Apply(IEvent verenigingWerdGemarkeerdAlsDubbel, PubliekVerenigingDetailDocument document)
+ {
+ document.Status = VerenigingStatus.Dubbel;
+ document.IsDubbelVan = verenigingWerdGemarkeerdAlsDubbel.Data.VCodeAuthentiekeVereniging;
+ }
+
+ public static void Apply(IEvent verenigingAanvaardeDubbeleVereniging, PubliekVerenigingDetailDocument document)
+ {
+ document.CorresponderendeVCodes =
+ document.CorresponderendeVCodes
+ .Append(verenigingAanvaardeDubbeleVereniging.Data.VCodeDubbeleVereniging)
+ .ToArray();
+ }
}
diff --git a/src/AssociationRegistry.Public.Schema/Constants/VerenigingStatus.cs b/src/AssociationRegistry.Public.Schema/Constants/VerenigingStatus.cs
index 9df97d25c..5bc4e6afc 100644
--- a/src/AssociationRegistry.Public.Schema/Constants/VerenigingStatus.cs
+++ b/src/AssociationRegistry.Public.Schema/Constants/VerenigingStatus.cs
@@ -4,4 +4,5 @@ public static class VerenigingStatus
{
public const string Actief = "Actief";
public const string Gestopt = "Gestopt";
+ public const string Dubbel = "Dubbel";
}
diff --git a/src/AssociationRegistry.Public.Schema/Detail/VerenigingDetailDocument.cs b/src/AssociationRegistry.Public.Schema/Detail/VerenigingDetailDocument.cs
index 674ef7645..58cf0a2b4 100644
--- a/src/AssociationRegistry.Public.Schema/Detail/VerenigingDetailDocument.cs
+++ b/src/AssociationRegistry.Public.Schema/Detail/VerenigingDetailDocument.cs
@@ -24,18 +24,15 @@ public class PubliekVerenigingDetailDocument : IVCode, ISoftDeleted, ICanBeUitge
public string Status { get; set; } = null!;
public string DatumLaatsteAanpassing { get; set; } = null!;
public Locatie[] Locaties { get; set; } = null!;
- public Contactgegeven[] Contactgegevens { get; set; } = Array.Empty();
+ public Contactgegeven[] Contactgegevens { get; set; } = [];
- public HoofdactiviteitVerenigingsloket[] HoofdactiviteitenVerenigingsloket { get; set; } =
- Array.Empty();
+ public HoofdactiviteitVerenigingsloket[] HoofdactiviteitenVerenigingsloket { get; set; } = [];
- public Werkingsgebied[] Werkingsgebieden { get; set; } =
- Array.Empty();
-
- public Sleutel[] Sleutels { get; set; } = Array.Empty();
- public Relatie[] Relaties { get; set; } = Array.Empty();
- public Lidmaatschap[] Lidmaatschappen { get; set; } = Array.Empty();
+ public Werkingsgebied[] Werkingsgebieden { get; set; } = [];
+ public Sleutel[] Sleutels { get; set; } = [];
+ public Relatie[] Relaties { get; set; } = [];
+ public Lidmaatschap[] Lidmaatschappen { get; set; } = [];
public bool? IsUitgeschrevenUitPubliekeDatastroom { get; set; }
[Identity] public string VCode { get; set; } = null!;
@@ -130,7 +127,6 @@ public record Lidmaatschap(
string Identificatie,
string Beschrijving);
-
public class AdresId
{
public string? Broncode { get; set; }
@@ -150,6 +146,8 @@ public class Adres
public bool Deleted { get; set; }
public DateTimeOffset? DeletedAt { get; set; }
+ public string IsDubbelVan { get; set; } = string.Empty;
+ public string[] CorresponderendeVCodes { get; set; } = [];
}
public class JsonLdMetadata
diff --git a/src/AssociationRegistry/Acties/AanvaardDubbel/AanvaardDubbeleVerenigingCommand.cs b/src/AssociationRegistry/Acties/AanvaardDubbel/AanvaardDubbeleVerenigingCommand.cs
new file mode 100644
index 000000000..6c6d62af8
--- /dev/null
+++ b/src/AssociationRegistry/Acties/AanvaardDubbel/AanvaardDubbeleVerenigingCommand.cs
@@ -0,0 +1,5 @@
+namespace AssociationRegistry.Acties.AanvaardDubbel;
+
+using AssociationRegistry.Vereniging;
+
+public record AanvaardDubbeleVerenigingCommand(VCode VCode, VCode VCodeDubbeleVereniging);
diff --git a/src/AssociationRegistry/Acties/AanvaardDubbel/AanvaardDubbeleVerenigingCommandHandler.cs b/src/AssociationRegistry/Acties/AanvaardDubbel/AanvaardDubbeleVerenigingCommandHandler.cs
new file mode 100644
index 000000000..6af8e9bb4
--- /dev/null
+++ b/src/AssociationRegistry/Acties/AanvaardDubbel/AanvaardDubbeleVerenigingCommandHandler.cs
@@ -0,0 +1,23 @@
+namespace AssociationRegistry.Acties.AanvaardDubbel;
+
+using AssociationRegistry.EventStore;
+using AssociationRegistry.Framework;
+using AssociationRegistry.Vereniging;
+using NodaTime;
+
+public class AanvaardDubbeleVerenigingCommandHandler(IVerenigingsRepository repository)
+{
+ public async Task Handle(AanvaardDubbeleVerenigingCommand command, CancellationToken cancellationToken)
+ {
+ var vereniging = await repository.Load(command.VCode);
+
+ vereniging.AanvaardDubbeleVereniging(command.VCodeDubbeleVereniging);
+
+ await repository.Save(
+ vereniging,
+ new CommandMetadata(EventStore.DigitaalVlaanderenOvoNumber,
+ SystemClock.Instance.GetCurrentInstant(),
+ Guid.NewGuid()),
+ cancellationToken);
+ }
+}
diff --git a/src/AssociationRegistry/Acties/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanCommand.cs b/src/AssociationRegistry/Acties/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanCommand.cs
new file mode 100644
index 000000000..77b1b36f5
--- /dev/null
+++ b/src/AssociationRegistry/Acties/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanCommand.cs
@@ -0,0 +1,5 @@
+namespace AssociationRegistry.Acties.MarkeerAlsDubbelVan;
+
+using Vereniging;
+
+public record MarkeerAlsDubbelVanCommand(VCode VCode, VCode VCodeAuthentiekeVereniging);
diff --git a/src/AssociationRegistry/Acties/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanCommandHandler.cs b/src/AssociationRegistry/Acties/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanCommandHandler.cs
new file mode 100644
index 000000000..6afd1616e
--- /dev/null
+++ b/src/AssociationRegistry/Acties/MarkeerAlsDubbelVan/MarkeerAlsDubbelVanCommandHandler.cs
@@ -0,0 +1,47 @@
+namespace AssociationRegistry.Acties.MarkeerAlsDubbelVan;
+
+using Framework;
+using Marten;
+using Messages;
+using Vereniging;
+using Vereniging.Exceptions;
+using Wolverine.Marten;
+
+public class MarkeerAlsDubbelVanCommandHandler
+{
+ private readonly IVerenigingsRepository _verenigingsRepository;
+ private readonly IMartenOutbox _outbox;
+ private readonly IDocumentSession _session;
+
+ public MarkeerAlsDubbelVanCommandHandler(
+ IVerenigingsRepository verenigingsRepository,
+ IMartenOutbox outbox,
+ IDocumentSession session
+ )
+ {
+ _verenigingsRepository = verenigingsRepository;
+ _outbox = outbox;
+ _session = session;
+ }
+
+ public async Task Handle(
+ CommandEnvelope message,
+ CancellationToken cancellationToken = default)
+ {
+ var vereniging = await _verenigingsRepository.Load(message.Command.VCode, message.Metadata.ExpectedVersion);
+
+ if (await _verenigingsRepository.IsVerwijderd(message.Command.VCodeAuthentiekeVereniging))
+ throw new VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging();
+
+ if (await _verenigingsRepository.IsDubbel(message.Command.VCodeAuthentiekeVereniging))
+ throw new VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel();
+
+ vereniging.MarkeerAlsDubbelVan(message.Command.VCodeAuthentiekeVereniging);
+
+ await _outbox.SendAsync(new AanvaardDubbeleVerenigingMessage(message.Command.VCodeAuthentiekeVereniging, message.Command.VCode));
+
+ var result = await _verenigingsRepository.Save(vereniging, _session, message.Metadata, cancellationToken);
+
+ return CommandResult.Create(message.Command.VCode, result);
+ }
+}
diff --git a/src/AssociationRegistry/EventStore/VerenigingsRepository.cs b/src/AssociationRegistry/EventStore/VerenigingsRepository.cs
index d047752f6..59354e86a 100644
--- a/src/AssociationRegistry/EventStore/VerenigingsRepository.cs
+++ b/src/AssociationRegistry/EventStore/VerenigingsRepository.cs
@@ -74,4 +74,18 @@ private void ThrowIfVerwijderd(VerenigingState verenigingState)
if (verenigingState.IsVerwijderd)
throw new VerenigingWerdVerwijderd(verenigingState.VCode);
}
+
+ public async Task IsDubbel(VCode vCode)
+ {
+ var verenigingState = await _eventStore.Load(vCode, null);
+
+ return verenigingState.IsDubbel;
+ }
+
+ private void ThrowIfDubbel(VerenigingState verenigingState)
+ {
+ if (verenigingState.IsDubbel)
+ throw new VerenigingWerdVerwijderd(verenigingState.VCode);
+ }
+
}
diff --git a/src/AssociationRegistry/Events/VerenigingWerdGermarkeerdAlsDubbelVan.cs b/src/AssociationRegistry/Events/VerenigingWerdGermarkeerdAlsDubbelVan.cs
new file mode 100644
index 000000000..439e09ffe
--- /dev/null
+++ b/src/AssociationRegistry/Events/VerenigingWerdGermarkeerdAlsDubbelVan.cs
@@ -0,0 +1,10 @@
+namespace AssociationRegistry.Events;
+
+using Framework;
+using Vereniging;
+
+public record VerenigingWerdGermarkeerdAlsDubbelVan(string VCode, string VCodeAuthentiekeVereniging) : IEvent
+{
+ public static VerenigingWerdGermarkeerdAlsDubbelVan With(VCode vCode, VCode vCodeAuthentiekeVereniging)
+ => new(vCode, vCodeAuthentiekeVereniging);
+}
diff --git a/src/AssociationRegistry/Events/VerenigingWerdToegevoegdAlsDubbel.cs b/src/AssociationRegistry/Events/VerenigingWerdToegevoegdAlsDubbel.cs
new file mode 100644
index 000000000..39e119df2
--- /dev/null
+++ b/src/AssociationRegistry/Events/VerenigingWerdToegevoegdAlsDubbel.cs
@@ -0,0 +1,12 @@
+namespace AssociationRegistry.Events;
+
+using Framework;
+using Vereniging;
+
+public record VerenigingAanvaardeDubbeleVereniging(string VCode, string VCodeDubbeleVereniging) : IEvent
+{
+ public static VerenigingAanvaardeDubbeleVereniging With(VCode vCode, VCode dubbeleVereniging)
+ => new(vCode, dubbeleVereniging);
+}
+
+
diff --git a/src/AssociationRegistry/Messages/AanvaardDubbeleVerenigingMessage.cs b/src/AssociationRegistry/Messages/AanvaardDubbeleVerenigingMessage.cs
new file mode 100644
index 000000000..f0997bb00
--- /dev/null
+++ b/src/AssociationRegistry/Messages/AanvaardDubbeleVerenigingMessage.cs
@@ -0,0 +1,10 @@
+namespace AssociationRegistry.Messages;
+
+using Acties.AanvaardDubbel;
+using AssociationRegistry.Vereniging;
+
+public record AanvaardDubbeleVerenigingMessage(string VCode, string VCodeDubbeleVereniging)
+{
+ public AanvaardDubbeleVerenigingCommand ToCommand()
+ => new(AssociationRegistry.Vereniging.VCode.Create(VCode), AssociationRegistry.Vereniging.VCode.Create(VCodeDubbeleVereniging));
+}
diff --git a/src/AssociationRegistry/Resources/ExceptionMessages.Designer.cs b/src/AssociationRegistry/Resources/ExceptionMessages.Designer.cs
index be1d49c9e..3302dfe80 100644
--- a/src/AssociationRegistry/Resources/ExceptionMessages.Designer.cs
+++ b/src/AssociationRegistry/Resources/ExceptionMessages.Designer.cs
@@ -646,6 +646,33 @@ public static string UnsupportedOperationForVerenigingstype {
}
}
+ ///
+ /// Looks up a localized string similar to Een vereniging kan geen dubbel worden van vereniging die zelf al een dubbel is van een andere vereniging..
+ ///
+ public static string VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel {
+ get {
+ return ResourceManager.GetString("VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Een vereniging kan geen dubbel worden van een verwijderde vereniging..
+ ///
+ public static string VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging {
+ get {
+ return ResourceManager.GetString("VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Een vereniging kan geen dubbel worden van zichzelf..
+ ///
+ public static string VerenigingKanGeenDubbelWordenVanZichzelf {
+ get {
+ return ResourceManager.GetString("VerenigingKanGeenDubbelWordenVanZichzelf", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Een vereniging kan geen lid worden van een verwijderde vereniging..
///
diff --git a/src/AssociationRegistry/Resources/ExceptionMessages.resx b/src/AssociationRegistry/Resources/ExceptionMessages.resx
index e91eee846..de766dd98 100644
--- a/src/AssociationRegistry/Resources/ExceptionMessages.resx
+++ b/src/AssociationRegistry/Resources/ExceptionMessages.resx
@@ -187,6 +187,15 @@
Een vereniging kan geen lid worden van een verwijderde vereniging.
+
+ Een vereniging kan geen dubbel worden van een verwijderde vereniging.
+
+
+ Een vereniging kan geen dubbel worden van vereniging die zelf al een dubbel is van een andere vereniging.
+
+
+ Een vereniging kan geen dubbel worden van zichzelf.
+
Een lidmaatschap mag niet overlappen voor eenzelfde vereniging.
diff --git a/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel.cs b/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel.cs
new file mode 100644
index 000000000..89ab75685
--- /dev/null
+++ b/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel.cs
@@ -0,0 +1,17 @@
+namespace AssociationRegistry.Vereniging.Exceptions;
+
+using Be.Vlaanderen.Basisregisters.AggregateSource;
+using Resources;
+using System.Runtime.Serialization;
+
+[Serializable]
+public class VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel : DomainException
+{
+ public VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel() : base(ExceptionMessages.VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel)
+ {
+ }
+
+ protected VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+}
diff --git a/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging.cs b/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging.cs
new file mode 100644
index 000000000..a2b6167c1
--- /dev/null
+++ b/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging.cs
@@ -0,0 +1,17 @@
+namespace AssociationRegistry.Vereniging.Exceptions;
+
+using Be.Vlaanderen.Basisregisters.AggregateSource;
+using Resources;
+using System.Runtime.Serialization;
+
+[Serializable]
+public class VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging : DomainException
+{
+ public VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging() : base(ExceptionMessages.VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging)
+ {
+ }
+
+ protected VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+}
diff --git a/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanZichzelf.cs b/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanZichzelf.cs
new file mode 100644
index 000000000..47a3504a6
--- /dev/null
+++ b/src/AssociationRegistry/Vereniging/Exceptions/VerenigingKanGeenDubbelWordenVanZichzelf.cs
@@ -0,0 +1,29 @@
+namespace AssociationRegistry.Vereniging.Exceptions;
+
+using Be.Vlaanderen.Basisregisters.AggregateSource;
+using Resources;
+using System.Runtime.Serialization;
+
+[Serializable]
+public class VerenigingKanGeenDubbelWordenVanZichzelf : DomainException
+{
+ public VerenigingKanGeenDubbelWordenVanZichzelf() : base(ExceptionMessages.VerenigingKanGeenDubbelWordenVanZichzelf)
+ {
+ }
+
+ protected VerenigingKanGeenDubbelWordenVanZichzelf(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+}
+
+[Serializable]
+public class InvalidOperationVerenigingKanGeenDubbelWordenVanZichzelf : ApplicationException
+{
+ public InvalidOperationVerenigingKanGeenDubbelWordenVanZichzelf() : base(ExceptionMessages.VerenigingKanGeenDubbelWordenVanZichzelf)
+ {
+ }
+
+ protected InvalidOperationVerenigingKanGeenDubbelWordenVanZichzelf(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+}
diff --git a/src/AssociationRegistry/Vereniging/IVerenigingsRepository.cs b/src/AssociationRegistry/Vereniging/IVerenigingsRepository.cs
index f2572b8ea..03ff7c6b9 100644
--- a/src/AssociationRegistry/Vereniging/IVerenigingsRepository.cs
+++ b/src/AssociationRegistry/Vereniging/IVerenigingsRepository.cs
@@ -11,4 +11,5 @@ public interface IVerenigingsRepository
Task Load(VCode vCode, long? expectedVersion = null) where TVereniging : IHydrate, new();
Task Load(KboNummer kboNummer, long? expectedVersion = null);
Task IsVerwijderd(VCode vCode);
+ Task IsDubbel(VCode vCode);
}
diff --git a/src/AssociationRegistry/Vereniging/Vereniging.cs b/src/AssociationRegistry/Vereniging/Vereniging.cs
index e9aaa3bb8..e38e3f9e0 100644
--- a/src/AssociationRegistry/Vereniging/Vereniging.cs
+++ b/src/AssociationRegistry/Vereniging/Vereniging.cs
@@ -232,6 +232,18 @@ public void SchrijfInInPubliekeDatastroom()
AddEvent(new VerenigingWerdIngeschrevenInPubliekeDatastroom());
}
+ public void MarkeerAlsDubbelVan(VCode isDubbelVan)
+ {
+ Throw.If(isDubbelVan.Equals(VCode));
+ AddEvent(VerenigingWerdGermarkeerdAlsDubbelVan.With(VCode, isDubbelVan));
+ }
+
+ public void AanvaardDubbeleVereniging(VCode dubbeleVereniging)
+ {
+ Throw.If(dubbeleVereniging.Equals(VCode));
+ AddEvent(VerenigingAanvaardeDubbeleVereniging.With(VCode, dubbeleVereniging));
+ }
+
public void Hydrate(VerenigingState obj)
{
Throw.If(obj.Verenigingstype != Verenigingstype.FeitelijkeVereniging);
diff --git a/src/AssociationRegistry/Vereniging/VerenigingState.cs b/src/AssociationRegistry/Vereniging/VerenigingState.cs
index 9111188a3..a7e996133 100644
--- a/src/AssociationRegistry/Vereniging/VerenigingState.cs
+++ b/src/AssociationRegistry/Vereniging/VerenigingState.cs
@@ -41,6 +41,9 @@ public string Identity
public bool IsIngeschrevenOpWijzigingenUitKbo { get; private init; }
public List HandledIdempotenceKeys { get; set; } = new();
public bool IsVerwijderd { get; set; }
+ public bool IsDubbel { get; set; }
+
+ public string[] CorresponderendeVCodes { get; set; } = [];
public long Version { get; set; }
public VerenigingState Apply(FeitelijkeVerenigingWerdGeregistreerd @event)
@@ -661,4 +664,16 @@ public VerenigingState Apply(LocatieDuplicaatWerdVerwijderdNaAdresMatch @event)
public VerenigingState Apply(AdresHeeftGeenVerschillenMetAdressenregister @event)
=> this;
+
+ public VerenigingState Apply(VerenigingWerdGermarkeerdAlsDubbelVan @event)
+ => this with
+ {
+ IsDubbel = true,
+ };
+
+ public VerenigingState Apply(VerenigingAanvaardeDubbeleVereniging @event)
+ => this with
+ {
+ CorresponderendeVCodes = CorresponderendeVCodes.Append(@event.VCodeDubbeleVereniging).ToArray(),
+ };
}
diff --git a/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/CommandHandling/Given_A_Vereniging.cs b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/CommandHandling/Given_A_Vereniging.cs
new file mode 100644
index 000000000..41a768862
--- /dev/null
+++ b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/CommandHandling/Given_A_Vereniging.cs
@@ -0,0 +1,75 @@
+namespace AssociationRegistry.Test.Admin.Api.Commands.FeitelijkeVereniging.When_MarkeerAlsDubbelVan.CommandHandling;
+
+using Acties.MarkeerAlsDubbelVan;
+using AssociationRegistry.Framework;
+using AutoFixture;
+using Common.AutoFixture;
+using Common.Framework;
+using Common.Scenarios.CommandHandling;
+using Events;
+using FluentAssertions;
+using GrarConsumer.FusieEvents.When_Consuming_Merger_Events;
+using Marten;
+using Messages;
+using Moq;
+using Vereniging;
+using Wolverine.Marten;
+using Xunit;
+using Xunit.Categories;
+
+[UnitTest]
+public class Given_A_Vereniging
+{
+ private readonly Fixture _fixture;
+ private readonly FeitelijkeVerenigingWerdGeregistreerdScenario _scenario;
+ private readonly VerenigingRepositoryMock _verenigingRepositoryMock;
+ private readonly MarkeerAlsDubbelVanCommandHandler _commandHandler;
+ private AanvaardDubbeleVerenigingMessage _outboxMessage;
+
+ public Given_A_Vereniging()
+ {
+ _fixture = new Fixture().CustomizeDomain();
+ _scenario = new FeitelijkeVerenigingWerdGeregistreerdScenario();
+ _verenigingRepositoryMock = new VerenigingRepositoryMock(_scenario.GetVerenigingState());
+
+ var martenOutbox = new Mock();
+ martenOutbox.CaptureOutboxSendAsyncMessage(message => _outboxMessage = message);
+
+ _commandHandler = new MarkeerAlsDubbelVanCommandHandler(
+ _verenigingRepositoryMock,
+ martenOutbox.Object,
+ Mock.Of()
+ );
+ }
+
+ [Fact]
+ public async Task Then_It_Saves_An_VerenigingWerdGermarkeerdAlsDubbel_Event()
+ {
+ var command = _fixture.Create() with
+ {
+ VCode = _scenario.VCode,
+ VCodeAuthentiekeVereniging = _fixture.Create(),
+ };
+
+ await _commandHandler.Handle(new CommandEnvelope(command, _fixture.Create()));
+
+ _verenigingRepositoryMock.ShouldHaveSaved(
+ new VerenigingWerdGermarkeerdAlsDubbelVan(
+ _scenario.VCode,
+ command.VCodeAuthentiekeVereniging));
+ }
+
+ [Fact]
+ public async Task Then_It_Sends_A_Message_To_The_Outbox()
+ {
+ var command = _fixture.Create() with
+ {
+ VCode = _scenario.VCode,
+ VCodeAuthentiekeVereniging = _fixture.Create(),
+ };
+
+ await _commandHandler.Handle(new CommandEnvelope(command, _fixture.Create()));
+
+ _outboxMessage.Should().BeEquivalentTo(new AanvaardDubbeleVerenigingMessage(command.VCodeAuthentiekeVereniging, command.VCode));
+ }
+}
diff --git a/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/CommandHandling/Given_VCode_Equals_IsDubbelVan.cs b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/CommandHandling/Given_VCode_Equals_IsDubbelVan.cs
new file mode 100644
index 000000000..4e46ed873
--- /dev/null
+++ b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/CommandHandling/Given_VCode_Equals_IsDubbelVan.cs
@@ -0,0 +1,56 @@
+namespace AssociationRegistry.Test.Admin.Api.Commands.FeitelijkeVereniging.When_MarkeerAlsDubbelVan.CommandHandling;
+
+using Acties.MarkeerAlsDubbelVan;
+using AssociationRegistry.Framework;
+using AutoFixture;
+using Common.AutoFixture;
+using Common.Framework;
+using Common.Scenarios.CommandHandling;
+using Events;
+using FluentAssertions;
+using Marten;
+using Moq;
+using Resources;
+using Vereniging;
+using Vereniging.Exceptions;
+using Wolverine.Marten;
+using Xunit;
+using Xunit.Categories;
+
+[UnitTest]
+public class Given_VCode_Equals_IsDubbelVan
+{
+ private readonly Fixture _fixture;
+ private readonly FeitelijkeVerenigingWerdGeregistreerdScenario _scenario;
+ private readonly VerenigingRepositoryMock _verenigingRepositoryMock;
+ private readonly MarkeerAlsDubbelVanCommandHandler _commandHandler;
+
+ public Given_VCode_Equals_IsDubbelVan()
+ {
+ _fixture = new Fixture().CustomizeDomain();
+ _scenario = new FeitelijkeVerenigingWerdGeregistreerdScenario();
+ _verenigingRepositoryMock = new VerenigingRepositoryMock(_scenario.GetVerenigingState());
+
+ _commandHandler = new MarkeerAlsDubbelVanCommandHandler(
+ _verenigingRepositoryMock,
+ Mock.Of(),
+ Mock.Of()
+ );
+ }
+
+ [Fact]
+ public async Task Then_Throws_VerenigingKanGeenDubbelWordenVanZichzelf()
+ {
+ var command = _fixture.Create() with
+ {
+ VCode = _scenario.VCode,
+ VCodeAuthentiekeVereniging = _scenario.VCode,
+ };
+
+ var exception = await Assert
+ .ThrowsAsync
+ (async () => await _commandHandler.Handle(
+ new CommandEnvelope(command, _fixture.Create())));
+ exception.Message.Should().Be(ExceptionMessages.VerenigingKanGeenDubbelWordenVanZichzelf);
+ }
+}
diff --git a/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestMapping/To_A_MarkeerAlsDubbelVanCommand.cs b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestMapping/To_A_MarkeerAlsDubbelVanCommand.cs
new file mode 100644
index 000000000..dd7bdd0a5
--- /dev/null
+++ b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestMapping/To_A_MarkeerAlsDubbelVanCommand.cs
@@ -0,0 +1,32 @@
+namespace AssociationRegistry.Test.Admin.Api.Commands.FeitelijkeVereniging.When_MarkeerAlsDubbelVan.RequestMapping;
+
+using AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.RequestModels;
+using AutoFixture;
+using Common.AutoFixture;
+using FluentAssertions;
+using Vereniging;
+using Xunit;
+using Xunit.Categories;
+
+[UnitTest]
+public class To_A_MarkeerAlsDubbelVanCommand
+{
+ [Fact]
+ public void Then_We_Get_A_MarkeerAlsDubbelVanCommand()
+ {
+ var fixture = new Fixture().CustomizeAdminApi();
+
+ var vCodeA = fixture.Create();
+ var vCodeB = fixture.Create();
+
+ var request = new MarkeerAlsDubbelVanRequest
+ {
+ IsDubbelVan = vCodeB.ToString(),
+ };
+
+ var actual = request.ToCommand(vCodeA);
+
+ actual.VCode.Should().BeEquivalentTo(vCodeA);
+ actual.VCodeAuthentiekeVereniging.Should().BeEquivalentTo(vCodeB);
+ }
+}
diff --git a/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Empty.cs b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Empty.cs
new file mode 100644
index 000000000..5b1f179e3
--- /dev/null
+++ b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Empty.cs
@@ -0,0 +1,25 @@
+namespace AssociationRegistry.Test.Admin.Api.Commands.FeitelijkeVereniging.When_MarkeerAlsDubbelVan.RequestValidating;
+
+using AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan;
+using AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.RequestModels;
+using FluentValidation.TestHelper;
+using Framework;
+using Xunit;
+
+public class Is_Empty : ValidatorTest
+{
+ [Fact]
+ public void Has_validation_error_IsDubbelVan_is_verplicht()
+ {
+ var validator = new MarkeerAlsDubbelVanValidator();
+
+ var request = new MarkeerAlsDubbelVanRequest
+ {
+ IsDubbelVan = "",
+ };
+ var result = validator.TestValidate(request);
+
+ result.ShouldHaveValidationErrorFor(toeRequest => toeRequest.IsDubbelVan)
+ .WithErrorMessage($"'{nameof(MarkeerAlsDubbelVanRequest.IsDubbelVan)}' is verplicht.");
+ }
+}
diff --git a/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Null.cs b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Null.cs
new file mode 100644
index 000000000..628d64510
--- /dev/null
+++ b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Null.cs
@@ -0,0 +1,25 @@
+namespace AssociationRegistry.Test.Admin.Api.Commands.FeitelijkeVereniging.When_MarkeerAlsDubbelVan.RequestValidating;
+
+using AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan;
+using AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.RequestModels;
+using FluentValidation.TestHelper;
+using Framework;
+using Xunit;
+
+public class Is_Null : ValidatorTest
+{
+ [Fact]
+ public void Has_validation_error_IsDubbelVan_is_verplicht()
+ {
+ var validator = new MarkeerAlsDubbelVanValidator();
+
+ var request = new MarkeerAlsDubbelVanRequest
+ {
+ IsDubbelVan = null,
+ };
+ var result = validator.TestValidate(request);
+
+ result.ShouldHaveValidationErrorFor(toeRequest => toeRequest.IsDubbelVan)
+ .WithErrorMessage($"'{nameof(MarkeerAlsDubbelVanRequest.IsDubbelVan)}' is verplicht.");
+ }
+}
diff --git a/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Valid.cs b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Valid.cs
new file mode 100644
index 000000000..ebb4e40ff
--- /dev/null
+++ b/test/AssociationRegistry.Test.Admin.Api/Commands/FeitelijkeVereniging/When_MarkeerAlsDubbelVan/RequestValidating/Is_Valid.cs
@@ -0,0 +1,25 @@
+namespace AssociationRegistry.Test.Admin.Api.Commands.FeitelijkeVereniging.When_MarkeerAlsDubbelVan.RequestValidating;
+
+using AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan;
+using AssociationRegistry.Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.RequestModels;
+using FluentValidation.TestHelper;
+using Framework;
+using Xunit;
+
+public class Is_Valid : ValidatorTest
+{
+ [Fact]
+ public void Has_no_validation_errors()
+ {
+ var validator = new MarkeerAlsDubbelVanValidator();
+
+ var request = new MarkeerAlsDubbelVanRequest
+ {
+ IsDubbelVan = "V0001001",
+ };
+
+ var result = validator.TestValidate(request);
+
+ result.ShouldNotHaveAnyValidationErrors();
+ }
+}
diff --git a/test/AssociationRegistry.Test.Admin.Api/Framework/templates/DetailVerenigingResponse.json b/test/AssociationRegistry.Test.Admin.Api/Framework/templates/DetailVerenigingResponse.json
index faf513aea..7681a921a 100644
--- a/test/AssociationRegistry.Test.Admin.Api/Framework/templates/DetailVerenigingResponse.json
+++ b/test/AssociationRegistry.Test.Admin.Api/Framework/templates/DetailVerenigingResponse.json
@@ -179,7 +179,8 @@
},
{{end}}
],
- "bron": "{{vereniging.bron}}"
+ "bron": "{{vereniging.bron}}",
+ "isDubbelVan":""
},
"metadata": {
"datumLaatsteAanpassing": "{{datumlaatsteaanpassing}}"
diff --git a/test/AssociationRegistry.Test.Admin.Api/GrarConsumer/FusieEvents/When_Consuming_Merger_Events/SetupMockExtension.cs b/test/AssociationRegistry.Test.Admin.Api/GrarConsumer/FusieEvents/When_Consuming_Merger_Events/SetupMockExtension.cs
index 79e70026b..594a02295 100644
--- a/test/AssociationRegistry.Test.Admin.Api/GrarConsumer/FusieEvents/When_Consuming_Merger_Events/SetupMockExtension.cs
+++ b/test/AssociationRegistry.Test.Admin.Api/GrarConsumer/FusieEvents/When_Consuming_Merger_Events/SetupMockExtension.cs
@@ -8,6 +8,8 @@
using Grar.GrarUpdates.Fusies.TeHeradresserenLocaties;
using Grar.GrarUpdates.Fusies.TeOntkoppelenLocaties;
using Moq;
+using Wolverine;
+using Wolverine.Marten;
public static class SetupMockExtension
{
@@ -16,6 +18,15 @@ public static void CaptureQueueOverkoepelendeGrarMessage(
Action action)
{
sqsClientWrapper.Setup(v => v.QueueMessage(It.IsAny()))
- .Callback(action);
+ .Callback(action);
+ }
+
+
+ public static void CaptureOutboxSendAsyncMessage(
+ this Mock outbox,
+ Action action)
+ {
+ outbox.Setup(v => v.SendAsync(It.IsAny(), It.IsAny()))
+ .Callback((arg1, _) => action(arg1));
}
}
diff --git a/test/AssociationRegistry.Test.Common/Framework/VerenigingRepositoryMock.cs b/test/AssociationRegistry.Test.Common/Framework/VerenigingRepositoryMock.cs
index 81dc65cd5..4bab731db 100644
--- a/test/AssociationRegistry.Test.Common/Framework/VerenigingRepositoryMock.cs
+++ b/test/AssociationRegistry.Test.Common/Framework/VerenigingRepositoryMock.cs
@@ -73,6 +73,9 @@ public async Task Load(KboNummer kboNummer,
public Task IsVerwijderd(VCode vCode)
=> Task.FromResult(false);
+ public Task IsDubbel(VCode vCode)
+ => Task.FromResult(false);
+
public void ShouldHaveLoaded(params string[] keys) where TVereniging : IHydrate, new()
{
_invocationsLoad.Should().BeEquivalentTo(
diff --git a/test/AssociationRegistry.Test.E2E/Scenarios/Requests/FeitelijkeVereniging/MarkeerAlsDubbelVanRequestFactory.cs b/test/AssociationRegistry.Test.E2E/Scenarios/Requests/FeitelijkeVereniging/MarkeerAlsDubbelVanRequestFactory.cs
new file mode 100644
index 000000000..79e37296d
--- /dev/null
+++ b/test/AssociationRegistry.Test.E2E/Scenarios/Requests/FeitelijkeVereniging/MarkeerAlsDubbelVanRequestFactory.cs
@@ -0,0 +1,41 @@
+namespace AssociationRegistry.Test.E2E.Scenarios.Requests.FeitelijkeVereniging;
+
+using Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.RequestModels;
+using Alba;
+using Framework.ApiSetup;
+using Givens.FeitelijkeVereniging;
+using Marten.Events;
+using System.Net;
+using Vereniging;
+
+public class MarkeerAlsDubbelVanRequestFactory : ITestRequestFactory
+{
+ private readonly MultipleWerdGeregistreerdScenario _scenario;
+
+ public MarkeerAlsDubbelVanRequestFactory(MultipleWerdGeregistreerdScenario scenario)
+ {
+ _scenario = scenario;
+ }
+
+ public async Task> ExecuteRequest(IApiSetup apiSetup)
+ {
+ var request = new MarkeerAlsDubbelVanRequest
+ {
+ IsDubbelVan = _scenario.AndereFeitelijkeVerenigingWerdGeregistreerd.VCode,
+ };
+
+ await apiSetup.AdminApiHost.Scenario(s =>
+ {
+ s.Post
+ .Json(request, JsonStyle.Mvc)
+ .ToUrl($"/v1/verenigingen/{_scenario.FeitelijkeVerenigingWerdGeregistreerd.VCode}/dubbelVan");
+
+ s.StatusCodeShouldBe(HttpStatusCode.Accepted);
+ });
+
+ await apiSetup.AdminProjectionHost.WaitForNonStaleProjectionDataAsync(TimeSpan.FromSeconds(60));
+ await apiSetup.PublicProjectionHost.WaitForNonStaleProjectionDataAsync(TimeSpan.FromSeconds(60));
+
+ return new RequestResult(VCode.Create(_scenario.FeitelijkeVerenigingWerdGeregistreerd.VCode), request);
+ }
+}
diff --git a/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Beheer/Detail/Returns_Detail_With_Dubbel_Van.cs b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Beheer/Detail/Returns_Detail_With_Dubbel_Van.cs
new file mode 100644
index 000000000..bb60d2a54
--- /dev/null
+++ b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Beheer/Detail/Returns_Detail_With_Dubbel_Van.cs
@@ -0,0 +1,41 @@
+namespace AssociationRegistry.Test.E2E.When_Markeer_Als_Dubbel_Van.Beheer.Detail;
+
+using Admin.Api.Verenigingen.Detail.ResponseModels;
+using Admin.Schema.Constants;
+using FluentAssertions;
+using Framework.AlbaHost;
+using Xunit;
+
+[Collection(FullBlownApiCollection.Name)]
+public class Returns_Detail_With_Dubbel_Van : IClassFixture, IAsyncLifetime
+{
+ private readonly MarkeerAlsDubbelVanContext _context;
+
+ public Returns_Detail_With_Dubbel_Van(MarkeerAlsDubbelVanContext context)
+ {
+ _context = context;
+ }
+
+ [Fact]
+ public void With_IsDubbelVan_VCode_Of_AndereFeitelijkeVerenigingWerdGeregistreerd()
+ {
+ Response.Vereniging.IsDubbelVan.Should().Be(_context.Scenario.AndereFeitelijkeVerenigingWerdGeregistreerd.VCode);
+ }
+
+ [Fact]
+ public void With_Status_Is_Dubbel()
+ {
+ Response.Vereniging.Status.Should().Be(VerenigingStatus.Dubbel);
+ }
+
+ public DetailVerenigingResponse Response { get; set; }
+
+ public async Task InitializeAsync()
+ {
+ Response = _context.ApiSetup.AdminApiHost.GetBeheerDetail(_context.VCode);
+ }
+
+ public async Task DisposeAsync()
+ {
+ }
+}
diff --git a/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Beheer/DetailAuthentiekeVereniging/Returns_Detail_AuthentiekeVereniging.cs b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Beheer/DetailAuthentiekeVereniging/Returns_Detail_AuthentiekeVereniging.cs
new file mode 100644
index 000000000..4b0c2b6eb
--- /dev/null
+++ b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Beheer/DetailAuthentiekeVereniging/Returns_Detail_AuthentiekeVereniging.cs
@@ -0,0 +1,66 @@
+namespace AssociationRegistry.Test.E2E.When_Markeer_Als_Dubbel_Van.Beheer.DetailAuthentiekeVereniging;
+
+using AssociationRegistry.Admin.Api.Verenigingen.Detail.ResponseModels;
+using AssociationRegistry.Admin.Schema.Constants;
+using AssociationRegistry.Test.E2E.Framework.AlbaHost;
+using FluentAssertions;
+using Xunit;
+using Xunit.Abstractions;
+
+[Collection(FullBlownApiCollection.Name)]
+public class Returns_Detail_AuthentiekeVereniging : IClassFixture, IAsyncLifetime
+{
+ private readonly MarkeerAlsDubbelVanContext _context;
+ private readonly ITestOutputHelper _helper;
+
+ public Returns_Detail_AuthentiekeVereniging(MarkeerAlsDubbelVanContext context, ITestOutputHelper helper)
+ {
+ _context = context;
+ _helper = helper;
+ }
+
+ [Fact]
+ public void With_IsDubbelVan_VCode_Of_AndereFeitelijkeVerenigingWerdGeregistreerd()
+ {
+ Response.Vereniging.IsDubbelVan.Should().BeEmpty();
+ }
+
+ [Fact]
+ public async Task With_DubbeleVereniging_In_CorresponderendeVCodes()
+ {
+ var tryCounter = 0;
+
+ while (tryCounter < 20)
+ {
+ ++tryCounter;
+ await Task.Delay(500);
+
+ _helper.WriteLine($"Looking for CorresponderendeVCodes (try {tryCounter})...");
+ if (Response.Vereniging.CorresponderendeVCodes.Any())
+ {
+ _helper.WriteLine("Found it!");
+ break;
+ }
+
+ _helper.WriteLine("Did not find any CorresponderendeVCodes.");
+ }
+ Response.Vereniging.CorresponderendeVCodes.Should().Contain(_context.Scenario.FeitelijkeVerenigingWerdGeregistreerd.VCode);
+ }
+
+ [Fact]
+ public void With_Status_Is_Actief()
+ {
+ Response.Vereniging.Status.Should().Be(VerenigingStatus.Actief);
+ }
+
+ public DetailVerenigingResponse Response { get; set; }
+
+ public async Task InitializeAsync()
+ {
+ Response = _context.ApiSetup.AdminApiHost.GetBeheerDetail(_context.Scenario.AndereFeitelijkeVerenigingWerdGeregistreerd.VCode);
+ }
+
+ public async Task DisposeAsync()
+ {
+ }
+}
diff --git a/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/MarkeerAlsDubbelVanContext.cs b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/MarkeerAlsDubbelVanContext.cs
new file mode 100644
index 000000000..aec3dbb6a
--- /dev/null
+++ b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/MarkeerAlsDubbelVanContext.cs
@@ -0,0 +1,31 @@
+namespace AssociationRegistry.Test.E2E.When_Markeer_Als_Dubbel_Van;
+
+using Admin.Api.Verenigingen.Dubbels.FeitelijkeVereniging.MarkeerAlsDubbelVan.RequestModels;
+using Framework.ApiSetup;
+using Framework.TestClasses;
+using Marten.Events;
+using Microsoft.Extensions.DependencyInjection;
+using Nest;
+using Scenarios.Givens.FeitelijkeVereniging;
+using Scenarios.Requests.FeitelijkeVereniging;
+using Vereniging;
+
+public class MarkeerAlsDubbelVanContext: TestContextBase
+{
+ public VCode VCode => RequestResult.VCode;
+ public MultipleWerdGeregistreerdScenario Scenario { get; }
+
+ public MarkeerAlsDubbelVanContext(FullBlownApiSetup apiSetup)
+ {
+ ApiSetup = apiSetup;
+ Scenario = new();
+ }
+
+ public override async Task InitializeAsync()
+ {
+ await ApiSetup.ExecuteGiven(Scenario);
+ RequestResult = await new MarkeerAlsDubbelVanRequestFactory(Scenario).ExecuteRequest(ApiSetup);
+ await ApiSetup.AdminProjectionHost.WaitForNonStaleProjectionDataAsync(TimeSpan.FromSeconds(10));
+ await ApiSetup.AdminApiHost.Services.GetRequiredService().Indices.RefreshAsync(Indices.All);
+ }
+}
diff --git a/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Publiek/Detail/Returns_Detail_With_Dubbel_Van.cs b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Publiek/Detail/Returns_Detail_With_Dubbel_Van.cs
new file mode 100644
index 000000000..d2058adc1
--- /dev/null
+++ b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Publiek/Detail/Returns_Detail_With_Dubbel_Van.cs
@@ -0,0 +1,41 @@
+namespace AssociationRegistry.Test.E2E.When_Markeer_Als_Dubbel_Van.Publiek.Detail;
+
+using FluentAssertions;
+using Framework.AlbaHost;
+using Public.Api.Verenigingen.Detail.ResponseModels;
+using Public.Schema.Constants;
+using Xunit;
+
+[Collection(FullBlownApiCollection.Name)]
+public class Returns_Detail_With_Dubbel_Van : IClassFixture, IAsyncLifetime
+{
+ private readonly MarkeerAlsDubbelVanContext _context;
+
+ public Returns_Detail_With_Dubbel_Van(MarkeerAlsDubbelVanContext context)
+ {
+ _context = context;
+ }
+
+ [Fact]
+ public void With_IsDubbelVan_VCode_Of_AndereFeitelijkeVerenigingWerdGeregistreerd()
+ {
+ Response.Vereniging.IsDubbelVan.Should().Be(_context.Scenario.AndereFeitelijkeVerenigingWerdGeregistreerd.VCode);
+ }
+
+ [Fact]
+ public void With_Status_Is_Dubbel()
+ {
+ Response.Vereniging.Status.Should().Be(VerenigingStatus.Dubbel);
+ }
+
+ public PubliekVerenigingDetailResponse Response { get; set; }
+
+ public async Task InitializeAsync()
+ {
+ Response = _context.ApiSetup.PublicApiHost.GetPubliekDetail(_context.VCode);
+ }
+
+ public async Task DisposeAsync()
+ {
+ }
+}
diff --git a/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Publiek/DetailAuthentiekeVereniging/Returns_Detail_AuthentiekeVereniging.cs b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Publiek/DetailAuthentiekeVereniging/Returns_Detail_AuthentiekeVereniging.cs
new file mode 100644
index 000000000..79aa9917d
--- /dev/null
+++ b/test/AssociationRegistry.Test.E2E/When_Markeer_Als_Dubbel_Van/Publiek/DetailAuthentiekeVereniging/Returns_Detail_AuthentiekeVereniging.cs
@@ -0,0 +1,66 @@
+namespace AssociationRegistry.Test.E2E.When_Markeer_Als_Dubbel_Van.Publiek.DetailAuthentiekeVereniging;
+
+using FluentAssertions;
+using Framework.AlbaHost;
+using Public.Api.Verenigingen.Detail.ResponseModels;
+using Public.Schema.Constants;
+using Xunit;
+using Xunit.Abstractions;
+
+[Collection(FullBlownApiCollection.Name)]
+public class Returns_Detail_AuthentiekeVereniging : IClassFixture, IAsyncLifetime
+{
+ private readonly MarkeerAlsDubbelVanContext _context;
+ private readonly ITestOutputHelper _helper;
+
+ public Returns_Detail_AuthentiekeVereniging(MarkeerAlsDubbelVanContext context, ITestOutputHelper helper)
+ {
+ _context = context;
+ _helper = helper;
+ }
+
+ [Fact]
+ public void With_IsDubbelVan_VCode_Of_AndereFeitelijkeVerenigingWerdGeregistreerd()
+ {
+ Response.Vereniging.IsDubbelVan.Should().BeEmpty();
+ }
+
+ [Fact]
+ public async Task With_DubbeleVereniging_In_CorresponderendeVCodes()
+ {
+ var tryCounter = 0;
+
+ while (tryCounter < 20)
+ {
+ ++tryCounter;
+ await Task.Delay(500);
+
+ _helper.WriteLine($"Looking for CorresponderendeVCodes (try {tryCounter})...");
+ if (Response.Vereniging.CorresponderendeVCodes.Any())
+ {
+ _helper.WriteLine("Found it!");
+ break;
+ }
+
+ _helper.WriteLine("Did not find any CorresponderendeVCodes.");
+ }
+ Response.Vereniging.CorresponderendeVCodes.Should().Contain(_context.Scenario.FeitelijkeVerenigingWerdGeregistreerd.VCode);
+ }
+
+ [Fact]
+ public void With_Status_Is_Actief()
+ {
+ Response.Vereniging.Status.Should().Be(VerenigingStatus.Actief);
+ }
+
+ public PubliekVerenigingDetailResponse Response { get; set; }
+
+ public async Task InitializeAsync()
+ {
+ Response = _context.ApiSetup.PublicApiHost.GetPubliekDetail(_context.Scenario.AndereFeitelijkeVerenigingWerdGeregistreerd.VCode);
+ }
+
+ public async Task DisposeAsync()
+ {
+ }
+}
diff --git a/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Beheer/Detail/Returns_DetailResponse.cs b/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Beheer/Detail/Returns_DetailResponse.cs
index 0ea8a34ef..0d59a1bac 100644
--- a/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Beheer/Detail/Returns_DetailResponse.cs
+++ b/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Beheer/Detail/Returns_DetailResponse.cs
@@ -79,6 +79,7 @@ public async Task WithFeitelijkeVereniging()
Relaties = MapRelaties([], TestContext.VCode),
Lidmaatschappen = [],
Sleutels = MapSleutels(Request, TestContext.VCode),
+ IsDubbelVan = string.Empty,
}, compareConfig: AdminDetailComparisonConfig.Instance);
private static Sleutel[] MapSleutels(RegistreerFeitelijkeVerenigingRequest request, string vCode)
diff --git a/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Publiek/Detail/Returns_DetailResponse.cs b/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Publiek/Detail/Returns_DetailResponse.cs
index 09ab14f24..faea515d3 100644
--- a/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Publiek/Detail/Returns_DetailResponse.cs
+++ b/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging/Publiek/Detail/Returns_DetailResponse.cs
@@ -78,6 +78,8 @@ public async Task WithFeitelijkeVereniging()
Locaties = MapLocaties(Request.Locaties, _testContext.VCode),
Relaties = MapRelaties([], _testContext.VCode),
Sleutels = MapSleutels(Request, _testContext.VCode),
+ IsDubbelVan = string.Empty,
+ CorresponderendeVCodes = [],
}, compareConfig: AdminDetailComparisonConfig.Instance);
private static Sleutel[] MapSleutels(RegistreerFeitelijkeVerenigingRequest request, string vCode)
diff --git a/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging_With_Potential_Duplicates/Beheer/Detail/Returns_DetailResponse.cs b/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging_With_Potential_Duplicates/Beheer/Detail/Returns_DetailResponse.cs
index 6aca743f9..041fe6084 100644
--- a/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging_With_Potential_Duplicates/Beheer/Detail/Returns_DetailResponse.cs
+++ b/test/AssociationRegistry.Test.E2E/When_Registreer_FeitelijkeVereniging_With_Potential_Duplicates/Beheer/Detail/Returns_DetailResponse.cs
@@ -79,6 +79,7 @@ public async Task WithFeitelijkeVereniging()
Relaties = MapRelaties([], TestContext.VCode),
Sleutels = MapSleutels(Request, TestContext.VCode),
Lidmaatschappen = [],
+ IsDubbelVan = string.Empty,
}, compareConfig: AdminDetailComparisonConfig.Instance);
private static Sleutel[] MapSleutels(RegistreerFeitelijkeVerenigingRequest request, string vCode)
diff --git a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Beheer/Detail/Returns_DetailResponse.cs b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Beheer/Detail/Returns_DetailResponse.cs
index f31d9b936..d0ba25fb0 100644
--- a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Beheer/Detail/Returns_DetailResponse.cs
+++ b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Beheer/Detail/Returns_DetailResponse.cs
@@ -82,5 +82,6 @@ public async Task WithFeitelijkeVereniging()
Relaties = BeheerDetailResponseMapper.MapRelaties([], TestContext.VCode),
Lidmaatschappen = [],
Sleutels = BeheerDetailResponseMapper.MapSleutels(Request, TestContext.VCode),
+ IsDubbelVan = string.Empty,
}, compareConfig: AdminDetailComparisonConfig.Instance);
}
diff --git a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Publiek/Detail/Returns_DetailResponse.cs b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Publiek/Detail/Returns_DetailResponse.cs
index 89e629600..aa1b1b433 100644
--- a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Publiek/Detail/Returns_DetailResponse.cs
+++ b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens/Publiek/Detail/Returns_DetailResponse.cs
@@ -71,6 +71,8 @@ public async Task WithFeitelijkeVereniging()
Locaties = PubliekDetailResponseMapper.MapLocaties(_testContext.RegistratieData.Locaties, _testContext.VCode),
Relaties = PubliekDetailResponseMapper.MapRelaties([], _testContext.VCode),
Sleutels = PubliekDetailResponseMapper.MapSleutels(Request, _testContext.VCode),
+ IsDubbelVan = string.Empty,
+ CorresponderendeVCodes = [],
}, compareConfig: AdminDetailComparisonConfig.Instance);
public override Func GetResponse
diff --git a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Beheer/Detail/Returns_DetailResponse.cs b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Beheer/Detail/Returns_DetailResponse.cs
index ee891fb4e..b267a8c8a 100644
--- a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Beheer/Detail/Returns_DetailResponse.cs
+++ b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Beheer/Detail/Returns_DetailResponse.cs
@@ -76,6 +76,7 @@ public async Task WithVerenigingMetRechtspersoonlijkheid()
Relaties = [],
Lidmaatschappen = [],
Sleutels = BeheerDetailResponseMapper.MapSleutels(TestContext.VCode, TestContext.RegistratieData.KboNummer),
+ IsDubbelVan = string.Empty,
}, compareConfig: AdminDetailComparisonConfig.Instance);
public override Func GetResponse
diff --git a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Publiek/Detail/Returns_DetailResponse.cs b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Publiek/Detail/Returns_DetailResponse.cs
index e708f5875..a0c6a55c7 100644
--- a/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Publiek/Detail/Returns_DetailResponse.cs
+++ b/test/AssociationRegistry.Test.E2E/When_Wijzig_Basisgegevens_Kbo/Publiek/Detail/Returns_DetailResponse.cs
@@ -71,6 +71,8 @@ public async Task WithVerenigingMetRechtspersoonlijkheid()
Locaties = [],
Relaties = [],
Sleutels = PubliekDetailResponseMapper.MapSleutels(_testContext.VCode, _testContext.RegistratieData.KboNummer),
+ IsDubbelVan = string.Empty,
+ CorresponderendeVCodes = [],
}, compareConfig: AdminDetailComparisonConfig.Instance);
public override Func GetResponse
diff --git a/test/AssociationRegistry.Test.Projections/Beheer/Detail/Dubbels/Given_VerenigingWerdGemarkeerdAlsDubbelVan.cs b/test/AssociationRegistry.Test.Projections/Beheer/Detail/Dubbels/Given_VerenigingWerdGemarkeerdAlsDubbelVan.cs
new file mode 100644
index 000000000..f65e7a9c7
--- /dev/null
+++ b/test/AssociationRegistry.Test.Projections/Beheer/Detail/Dubbels/Given_VerenigingWerdGemarkeerdAlsDubbelVan.cs
@@ -0,0 +1,21 @@
+namespace AssociationRegistry.Test.Projections.Beheer.Detail.Dubbels;
+
+using Admin.Schema.Constants;
+
+[Collection(nameof(ProjectionContext))]
+public class Given_VerenigingWerdGemarkeerdAlsDubbelVan(BeheerDetailScenarioFixture fixture)
+ : BeheerDetailScenarioClassFixture
+{
+ [Fact]
+ public void Metadata_Is_Updated()
+ => fixture.Result
+ .Metadata.Version.Should().Be(2);
+
+ [Fact]
+ public void Document_IsDubbelVan_Is_Updated()
+ => fixture.Result.IsDubbelVan.Should().Be(fixture.Scenario.VerenigingWerdGermarkeerdAlsDubbelVan.VCodeAuthentiekeVereniging);
+
+ [Fact]
+ public void Document_Status_Is_Dubbel()
+ => fixture.Result.Status.Should().Be(VerenigingStatus.Dubbel);
+}
diff --git a/test/AssociationRegistry.Test.Projections/Beheer/Detail/Dubbels/Given_VerenigingWerdToegevoegdAlsDubbel.cs b/test/AssociationRegistry.Test.Projections/Beheer/Detail/Dubbels/Given_VerenigingWerdToegevoegdAlsDubbel.cs
new file mode 100644
index 000000000..25f7ed1bc
--- /dev/null
+++ b/test/AssociationRegistry.Test.Projections/Beheer/Detail/Dubbels/Given_VerenigingWerdToegevoegdAlsDubbel.cs
@@ -0,0 +1,25 @@
+namespace AssociationRegistry.Test.Projections.Beheer.Detail.Dubbels;
+
+using Admin.Schema.Constants;
+
+[Collection(nameof(ProjectionContext))]
+public class Given_VerenigingWerdToegevoegdAlsDubbel(BeheerDetailScenarioFixture fixture)
+ : BeheerDetailScenarioClassFixture
+{
+ [Fact]
+ public void Metadata_Is_Updated()
+ => fixture.Result
+ .Metadata.Version.Should().Be(2);
+
+ [Fact]
+ public void Document_IsDubbelVan_Is_Updated()
+ => fixture.Result.IsDubbelVan.Should().BeEmpty();
+
+ [Fact]
+ public void Document_Status_Is_Actief()
+ => fixture.Result.Status.Should().Be(VerenigingStatus.Actief);
+
+ [Fact]
+ public void Document_Has_DubbeleVereniging_In_CorresponderendeVCodes()
+ => fixture.Result.CorresponderendeVCodes.Should().Contain(fixture.Scenario.DubbeleVerenigingWerdGeregistreerd.VCode);
+}
diff --git a/test/AssociationRegistry.Test.Projections/Publiek/Detail/Dubbels/Given_VerenigingWerdGemarkeerdAlsDubbelVan.cs b/test/AssociationRegistry.Test.Projections/Publiek/Detail/Dubbels/Given_VerenigingWerdGemarkeerdAlsDubbelVan.cs
new file mode 100644
index 000000000..62b8369c3
--- /dev/null
+++ b/test/AssociationRegistry.Test.Projections/Publiek/Detail/Dubbels/Given_VerenigingWerdGemarkeerdAlsDubbelVan.cs
@@ -0,0 +1,16 @@
+namespace AssociationRegistry.Test.Projections.Publiek.Detail.Dubbels;
+
+using Public.Schema.Constants;
+
+[Collection(nameof(ProjectionContext))]
+public class Given_VerenigingWerdGemarkeerdAlsDubbelVan(PubliekDetailScenarioFixture fixture)
+ : PubliekDetailScenarioClassFixture
+{
+ [Fact]
+ public void Document_IsDubbelVan_Is_Updated()
+ => fixture.Result.IsDubbelVan.Should().Be(fixture.Scenario.VerenigingWerdGermarkeerdAlsDubbelVan.VCodeAuthentiekeVereniging);
+
+ [Fact]
+ public void Document_Status_Is_Dubbel()
+ => fixture.Result.Status.Should().Be(VerenigingStatus.Dubbel);
+}
diff --git a/test/AssociationRegistry.Test.Projections/Publiek/Detail/Dubbels/Given_VerenigingWerdToegevoegdAlsDubbel.cs b/test/AssociationRegistry.Test.Projections/Publiek/Detail/Dubbels/Given_VerenigingWerdToegevoegdAlsDubbel.cs
new file mode 100644
index 000000000..33912bf77
--- /dev/null
+++ b/test/AssociationRegistry.Test.Projections/Publiek/Detail/Dubbels/Given_VerenigingWerdToegevoegdAlsDubbel.cs
@@ -0,0 +1,20 @@
+namespace AssociationRegistry.Test.Projections.Publiek.Detail.Dubbels;
+
+using Public.Schema.Constants;
+
+[Collection(nameof(ProjectionContext))]
+public class Given_VerenigingWerdToegevoegdAlsDubbel(PubliekDetailScenarioFixture fixture)
+ : PubliekDetailScenarioClassFixture
+{
+ [Fact]
+ public void Document_IsDubbelVan_Is_Updated()
+ => fixture.Result.IsDubbelVan.Should().BeEmpty();
+
+ [Fact]
+ public void Document_Status_Is_Actief()
+ => fixture.Result.Status.Should().Be(VerenigingStatus.Actief);
+
+ [Fact]
+ public void Document_Has_DubbeleVereniging_In_CorresponderendeVCodes()
+ => fixture.Result.CorresponderendeVCodes.Should().Contain(fixture.Scenario.DubbeleVerenigingWerdGeregistreerd.VCode);
+}
diff --git a/test/AssociationRegistry.Test.Projections/Scenario/VerenigingWerdGemarkeerdAlsDubbelVanScenario.cs b/test/AssociationRegistry.Test.Projections/Scenario/VerenigingWerdGemarkeerdAlsDubbelVanScenario.cs
new file mode 100644
index 000000000..c2d00ae38
--- /dev/null
+++ b/test/AssociationRegistry.Test.Projections/Scenario/VerenigingWerdGemarkeerdAlsDubbelVanScenario.cs
@@ -0,0 +1,37 @@
+namespace AssociationRegistry.Test.Projections.Scenario;
+
+using AutoFixture;
+using Events;
+
+public class VerenigingWerdGemarkeerdAlsDubbelVanScenario : ScenarioBase
+{
+ public FeitelijkeVerenigingWerdGeregistreerd DubbeleVerenigingWerdGeregistreerd { get; }
+ public FeitelijkeVerenigingWerdGeregistreerd AuthentiekeVerenigingWerdGeregistreerd { get; }
+ public VerenigingWerdGermarkeerdAlsDubbelVan VerenigingWerdGermarkeerdAlsDubbelVan { get; set; }
+ public VerenigingAanvaardeDubbeleVereniging VerenigingAanvaardeDubbeleVereniging { get; set; }
+
+ public VerenigingWerdGemarkeerdAlsDubbelVanScenario()
+ {
+ DubbeleVerenigingWerdGeregistreerd = AutoFixture.Create();
+ AuthentiekeVerenigingWerdGeregistreerd = AutoFixture.Create();
+
+ VerenigingWerdGermarkeerdAlsDubbelVan = AutoFixture.Create() with
+ {
+ VCode = DubbeleVerenigingWerdGeregistreerd.VCode,
+ };
+
+ VerenigingAanvaardeDubbeleVereniging = AutoFixture.Create() with
+ {
+ VCode = AuthentiekeVerenigingWerdGeregistreerd.VCode,
+ VCodeDubbeleVereniging = DubbeleVerenigingWerdGeregistreerd.VCode,
+ };
+ }
+
+ public override string VCode => DubbeleVerenigingWerdGeregistreerd.VCode;
+
+ public override EventsPerVCode[] Events =>
+ [
+ new(VCode, DubbeleVerenigingWerdGeregistreerd, VerenigingWerdGermarkeerdAlsDubbelVan),
+ new(AuthentiekeVerenigingWerdGeregistreerd.VCode, AuthentiekeVerenigingWerdGeregistreerd, VerenigingAanvaardeDubbeleVereniging),
+ ];
+}
diff --git a/test/AssociationRegistry.Test.Projections/Scenario/VerenigingWerdToegevoegdAlsDubbelScenario.cs b/test/AssociationRegistry.Test.Projections/Scenario/VerenigingWerdToegevoegdAlsDubbelScenario.cs
new file mode 100644
index 000000000..d0f29a8bb
--- /dev/null
+++ b/test/AssociationRegistry.Test.Projections/Scenario/VerenigingWerdToegevoegdAlsDubbelScenario.cs
@@ -0,0 +1,40 @@
+namespace AssociationRegistry.Test.Projections.Scenario;
+
+using AutoFixture;
+using Events;
+
+///
+/// This is exactly the same as VerenigingWerdGemarkeerdAlsDubbelVanScenario, but from the POV of the AuthentiekeVereniging
+///
+public class VerenigingWerdToegevoegdAlsDubbelScenario : ScenarioBase
+{
+ public FeitelijkeVerenigingWerdGeregistreerd DubbeleVerenigingWerdGeregistreerd { get; }
+ public FeitelijkeVerenigingWerdGeregistreerd AuthentiekeVerenigingWerdGeregistreerd { get; }
+ public VerenigingWerdGermarkeerdAlsDubbelVan VerenigingWerdGermarkeerdAlsDubbelVan { get; set; }
+ public VerenigingAanvaardeDubbeleVereniging VerenigingAanvaardeDubbeleVereniging { get; set; }
+
+ public VerenigingWerdToegevoegdAlsDubbelScenario()
+ {
+ DubbeleVerenigingWerdGeregistreerd = AutoFixture.Create();
+ AuthentiekeVerenigingWerdGeregistreerd = AutoFixture.Create();
+
+ VerenigingWerdGermarkeerdAlsDubbelVan = AutoFixture.Create() with
+ {
+ VCode = DubbeleVerenigingWerdGeregistreerd.VCode,
+ };
+
+ VerenigingAanvaardeDubbeleVereniging = AutoFixture.Create() with
+ {
+ VCode = AuthentiekeVerenigingWerdGeregistreerd.VCode,
+ VCodeDubbeleVereniging = DubbeleVerenigingWerdGeregistreerd.VCode,
+ };
+ }
+
+ public override string VCode => AuthentiekeVerenigingWerdGeregistreerd.VCode;
+
+ public override EventsPerVCode[] Events =>
+ [
+ new(DubbeleVerenigingWerdGeregistreerd.VCode, DubbeleVerenigingWerdGeregistreerd, VerenigingWerdGermarkeerdAlsDubbelVan),
+ new(AuthentiekeVerenigingWerdGeregistreerd.VCode, AuthentiekeVerenigingWerdGeregistreerd, VerenigingAanvaardeDubbeleVereniging),
+ ];
+}
diff --git a/test/AssociationRegistry.Test.Public.Api/templates/DetailVerenigingResponse.json b/test/AssociationRegistry.Test.Public.Api/templates/DetailVerenigingResponse.json
index d5b2d1196..555d14029 100644
--- a/test/AssociationRegistry.Test.Public.Api/templates/DetailVerenigingResponse.json
+++ b/test/AssociationRegistry.Test.Public.Api/templates/DetailVerenigingResponse.json
@@ -137,7 +137,9 @@
},
{{end}}
],
-"lidmaatschappen": []
+"lidmaatschappen": [],
+"isDubbelVan": "",
+"corresponderendeVCodes": []
},
"metadata": {
"datumLaatsteAanpassing": "{{datumlaatsteaanpassing}}"
diff --git a/test/AssociationRegistry.Test/When_AanvaardDubbeleVereniging/Given_VCode_And_VCodeDubbeleVereniging_Are_The_Same.cs b/test/AssociationRegistry.Test/When_AanvaardDubbeleVereniging/Given_VCode_And_VCodeDubbeleVereniging_Are_The_Same.cs
new file mode 100644
index 000000000..7e5dae7b5
--- /dev/null
+++ b/test/AssociationRegistry.Test/When_AanvaardDubbeleVereniging/Given_VCode_And_VCodeDubbeleVereniging_Are_The_Same.cs
@@ -0,0 +1,34 @@
+namespace AssociationRegistry.Test.When_AanvaardDubbeleVereniging;
+
+using AssociationRegistry.Acties.AanvaardDubbel;
+using AssociationRegistry.Test.Common.AutoFixture;
+using AssociationRegistry.Test.Common.Framework;
+using AssociationRegistry.Test.Common.Scenarios.CommandHandling;
+using AssociationRegistry.Vereniging;
+using AssociationRegistry.Vereniging.Exceptions;
+using AutoFixture;
+using FluentAssertions;
+using Resources;
+using Xunit;
+
+public class Given_VCode_And_VCodeDubbeleVereniging_Are_The_Same
+{
+ [Fact]
+ public async Task Then_Throws_InvalidOperationVerenigingKanGeenDubbelWordenVanZichzelf()
+ {
+ var fixture = new Fixture().CustomizeDomain();
+ var scenario = new FeitelijkeVerenigingWerdGeregistreerdScenario();
+ var repositoryMock = new VerenigingRepositoryMock(scenario.GetVerenigingState());
+ var vCode = fixture.Create();
+ var command = fixture.Create() with
+ {
+ VCode = scenario.VCode,
+ VCodeDubbeleVereniging = scenario.VCode,
+ };
+ var sut = new AanvaardDubbeleVerenigingCommandHandler(repositoryMock);
+
+ var exception = await Assert.ThrowsAsync(
+ async () => await sut.Handle(command, CancellationToken.None));
+ exception.Message.Should().Be(ExceptionMessages.VerenigingKanGeenDubbelWordenVanZichzelf);
+ }
+}
diff --git a/test/AssociationRegistry.Test/When_AanvaardDubbeleVereniging/Given_Valid_AanvaardDubbeleVerenigingCommand.cs b/test/AssociationRegistry.Test/When_AanvaardDubbeleVereniging/Given_Valid_AanvaardDubbeleVerenigingCommand.cs
new file mode 100644
index 000000000..1d85317dc
--- /dev/null
+++ b/test/AssociationRegistry.Test/When_AanvaardDubbeleVereniging/Given_Valid_AanvaardDubbeleVerenigingCommand.cs
@@ -0,0 +1,30 @@
+namespace AssociationRegistry.Test.When_AanvaardDubbeleVereniging;
+
+using AssociationRegistry.Acties.AanvaardDubbel;
+using AssociationRegistry.Events;
+using AssociationRegistry.Test.Common.AutoFixture;
+using AssociationRegistry.Test.Common.Framework;
+using AssociationRegistry.Test.Common.Scenarios.CommandHandling;
+using AutoFixture;
+using Xunit;
+
+public class Given_Valid_AanvaardDubbeleVerenigingCommand
+{
+ [Fact]
+ public async Task Then_Throws_InvalidOperationVerenigingKanGeenDubbelWordenVanZichzelf()
+ {
+ var fixture = new Fixture().CustomizeDomain();
+ var scenario = new FeitelijkeVerenigingWerdGeregistreerdScenario();
+ var repositoryMock = new VerenigingRepositoryMock(scenario.GetVerenigingState());
+ var command = fixture.Create()
+ with
+ {
+ VCode = scenario.VCode,
+ };
+ var sut = new AanvaardDubbeleVerenigingCommandHandler(repositoryMock);
+
+ await sut.Handle(command, CancellationToken.None);
+
+ repositoryMock.ShouldHaveSaved(new VerenigingAanvaardeDubbeleVereniging(scenario.VCode, command.VCodeDubbeleVereniging));
+ }
+}
diff --git a/test/AssociationRegistry.Test/When_Markeer_Als_Dubbel_Van/Given_IsDubbelVan_Vereniging_Is_Already_A_Dubbel.cs b/test/AssociationRegistry.Test/When_Markeer_Als_Dubbel_Van/Given_IsDubbelVan_Vereniging_Is_Already_A_Dubbel.cs
new file mode 100644
index 000000000..5aad95275
--- /dev/null
+++ b/test/AssociationRegistry.Test/When_Markeer_Als_Dubbel_Van/Given_IsDubbelVan_Vereniging_Is_Already_A_Dubbel.cs
@@ -0,0 +1,39 @@
+namespace AssociationRegistry.Test.When_Markeer_Als_Dubbel_Van;
+
+using Acties.MarkeerAlsDubbelVan;
+using AssociationRegistry.Framework;
+using AutoFixture;
+using Common.AutoFixture;
+using FluentAssertions;
+using Marten;
+using Moq;
+using Resources;
+using Vereniging;
+using Vereniging.Exceptions;
+using Wolverine.Marten;
+using Xunit;
+
+public class Given_IsDubbelVan_Vereniging_Is_Already_A_Dubbel
+{
+ [Fact]
+ public async Task Then_Throws_VerenigingKanGeenDubbelWordenVanDubbelVereniging()
+ {
+ var fixture = new Fixture().CustomizeDomain();
+ var verenigingsRepositoryMock = new Mock();
+ var command = fixture.Create();
+ var commandEnvelope = new CommandEnvelope(command, fixture.Create());
+
+ verenigingsRepositoryMock.Setup(s => s.IsDubbel(command.VCodeAuthentiekeVereniging))
+ .ReturnsAsync(true);
+
+ var sut = new MarkeerAlsDubbelVanCommandHandler(verenigingsRepositoryMock.Object,
+ Mock.Of(),
+ Mock.Of()
+ );
+
+ var exception = await Assert.ThrowsAsync
+ (async () => await sut.Handle(commandEnvelope, CancellationToken.None));
+
+ exception.Message.Should().Be(ExceptionMessages.VerenigingKanGeenDubbelWordenVanEenVerenigingReedsGemarkeerdAlsDubbel);
+ }
+}
diff --git a/test/AssociationRegistry.Test/When_Markeer_Als_Dubbel_Van/Given_IsDubbelVan_Vereniging_Is_Verwijderd.cs b/test/AssociationRegistry.Test/When_Markeer_Als_Dubbel_Van/Given_IsDubbelVan_Vereniging_Is_Verwijderd.cs
new file mode 100644
index 000000000..c949e5a30
--- /dev/null
+++ b/test/AssociationRegistry.Test/When_Markeer_Als_Dubbel_Van/Given_IsDubbelVan_Vereniging_Is_Verwijderd.cs
@@ -0,0 +1,39 @@
+namespace AssociationRegistry.Test.When_Markeer_Als_Dubbel_Van;
+
+using Acties.MarkeerAlsDubbelVan;
+using AssociationRegistry.Framework;
+using AutoFixture;
+using Common.AutoFixture;
+using FluentAssertions;
+using Marten;
+using Moq;
+using Resources;
+using Vereniging;
+using Vereniging.Exceptions;
+using Wolverine.Marten;
+using Xunit;
+
+public class Given_IsDubbelVan_Vereniging_Is_Verwijderd
+{
+ [Fact]
+ public async Task Then_Throws_VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging()
+ {
+ var fixture = new Fixture().CustomizeDomain();
+ var verenigingsRepositoryMock = new Mock();
+ var command = fixture.Create();
+ var commandEnvelope = new CommandEnvelope(command, fixture.Create());
+
+ verenigingsRepositoryMock.Setup(s => s.IsVerwijderd(command.VCodeAuthentiekeVereniging))
+ .ReturnsAsync(true);
+
+ var sut = new MarkeerAlsDubbelVanCommandHandler(verenigingsRepositoryMock.Object,
+ Mock.Of(),
+ Mock.Of()
+ );
+
+ var exception = await Assert.ThrowsAsync
+ (async () => await sut.Handle(commandEnvelope, CancellationToken.None));
+
+ exception.Message.Should().Be(ExceptionMessages.VerenigingKanGeenDubbelWordenVanVerwijderdeVereniging);
+ }
+}
diff --git a/test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_A_Second_Primair.cs b/test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_A_Second_Primair.cs
similarity index 100%
rename from test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_A_Second_Primair.cs
rename to test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_A_Second_Primair.cs
diff --git a/test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_All_Fields.cs b/test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_All_Fields.cs
similarity index 100%
rename from test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_All_Fields.cs
rename to test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_All_Fields.cs
diff --git a/test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_No_Changes.cs b/test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_No_Changes.cs
similarity index 100%
rename from test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_No_Changes.cs
rename to test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_No_Changes.cs
diff --git a/test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_Not_The_MaatschappelijkeZetelId.cs b/test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_Not_The_MaatschappelijkeZetelId.cs
similarity index 100%
rename from test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_Not_The_MaatschappelijkeZetelId.cs
rename to test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_Not_The_MaatschappelijkeZetelId.cs
diff --git a/test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_The_MaatschappelijkeZetelId.cs b/test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_The_MaatschappelijkeZetelId.cs
similarity index 100%
rename from test/AssociationRegistry.Test/WhenWijzigMaatschappelijkeZetel/Given_The_MaatschappelijkeZetelId.cs
rename to test/AssociationRegistry.Test/When_WijzigMaatschappelijkeZetel/Given_The_MaatschappelijkeZetelId.cs