diff --git a/src/AddressRegistry/StreetName/StreetNameReaddresser.cs b/src/AddressRegistry/StreetName/StreetNameReaddresser.cs index 6f4f99c61..bc44dd413 100644 --- a/src/AddressRegistry/StreetName/StreetNameReaddresser.cs +++ b/src/AddressRegistry/StreetName/StreetNameReaddresser.cs @@ -3,7 +3,6 @@ namespace AddressRegistry.StreetName using System; using System.Collections.Generic; using System.Linq; - using Address; using Commands; using DataStructures; using Exceptions; @@ -15,6 +14,10 @@ public sealed class StreetNameReaddresser private readonly IEnumerable _addressesToReaddress; private readonly StreetName _streetName; + + private readonly IDictionary> _sourceAddressesWithBoxNumbers = + new Dictionary>(); + public IEnumerable ReaddressedHouseNumbers => ReaddressedAddresses.Select(x => x.Value.readdressedHouseNumber); @@ -25,9 +28,7 @@ public IDictionary< AddressPersistentLocalId, ( ReaddressedAddressData readdressedHouseNumber, - List readdressedBoxNumbers, - List rejectedBoxNumberPersistentLocalIds, - List retiredBoxNumberPersistentLocalIds + List readdressedBoxNumbers )> ReaddressedAddresses { get; } public List<(ReaddressAction action, AddressPersistentLocalId addressPersistentLocalId)> Actions { get; } @@ -44,10 +45,7 @@ public StreetNameReaddresser( _streetName = streetName; ReaddressedAddresses = - new Dictionary readdressedBoxNumbers, List - rejectedBoxNumberPersistentLocalIds, List - retiredBoxNumberPersistentLocalIds)>(); + new Dictionary readdressedBoxNumbers)>(); Actions = new List<(ReaddressAction, AddressPersistentLocalId)>(); Readdress(); @@ -79,9 +77,7 @@ private void Readdress() sourceAddress.Geometry, sourceAddress.IsOfficiallyAssigned ), - new List(), - new List(), - new List() + new List() )); ReaddressBoxNumbers( @@ -95,6 +91,25 @@ private void Readdress() RejectOrRetireDestinationAddressBoxNumbers(destinationAddress); } } + + foreach (var sourceAddressWithBoxNumbers in _sourceAddressesWithBoxNumbers) + { + var sourceHouseNumberAddress = sourceAddressWithBoxNumbers.Key; + foreach (var sourceBoxNumberAddress in sourceAddressWithBoxNumbers.Value) + { + // 11 -> 13 -> 15 + // 11A 13A1 + // 11B 13B + // Given the above, + // When house number 11 is the current sourceAddress, it is never used as a destination address, so we should keep all of its box numbers. + // When house number 13 is the current sourceAddress, then ReaddressedAddresses will contain boxNumberAddress 13B and we should keep it. + if (IsUsedAsDestinationAddress(sourceHouseNumberAddress) && + !HasBeenReaddressed(sourceBoxNumberAddress)) + { + RejectOrRetireBoxNumberAddress(sourceBoxNumberAddress); + } + } + } } private (StreetNameAddress sourceAddress, StreetNameAddress? destinationAddress) GetSourceAndDestinationAddresses(ReaddressAddressItem addressToReaddress) @@ -165,15 +180,13 @@ private void ReaddressBoxNumbers( sourceBoxNumberAddress.IsOfficiallyAssigned )); - // 11 -> 13 -> 15 - // 11A 13A1 - // 11B 13B - // Given the above, - // When house number 11 is the current sourceAddress, it is never used as a destination address, so we should keep all of its box numbers. - // When house number 13 is the current sourceAddress, then ReaddressedAddresses will contain boxNumberAddress 13B and we should keep it. - if (IsUsedAsDestinationAddress(sourceAddress) && !HasBeenReaddressed(sourceBoxNumberAddress)) + if (_sourceAddressesWithBoxNumbers.ContainsKey(sourceAddress)) + { + _sourceAddressesWithBoxNumbers[sourceAddress].Add(sourceBoxNumberAddress); + } + else { - RejectOrRetireBoxNumberAddress(sourceBoxNumberAddress); + _sourceAddressesWithBoxNumbers.Add(sourceAddress, new List { sourceBoxNumberAddress }); } } } @@ -206,15 +219,11 @@ private void RejectOrRetireBoxNumberAddress(StreetNameAddress boxNumberAddress) switch (boxNumberAddress.Status) { case AddressStatus.Proposed: - Actions.Add((ReaddressAction.Reject, boxNumberAddress.AddressPersistentLocalId)); - ReaddressedAddresses[boxNumberAddress.Parent!.AddressPersistentLocalId] - .rejectedBoxNumberPersistentLocalIds.Add(boxNumberAddress.AddressPersistentLocalId); + Actions.Add((ReaddressAction.RejectBoxNumber, boxNumberAddress.AddressPersistentLocalId)); break; case AddressStatus.Current: - Actions.Add((ReaddressAction.Retire, boxNumberAddress.AddressPersistentLocalId)); - ReaddressedAddresses[boxNumberAddress.Parent!.AddressPersistentLocalId] - .retiredBoxNumberPersistentLocalIds.Add(boxNumberAddress.AddressPersistentLocalId); + Actions.Add((ReaddressAction.RetireBoxNumber, boxNumberAddress.AddressPersistentLocalId)); break; default: @@ -242,7 +251,7 @@ public enum ReaddressAction { ProposeHouseNumber, ProposeBoxNumber, - Reject, - Retire, + RejectBoxNumber, + RetireBoxNumber, } } diff --git a/src/AddressRegistry/StreetName/StreetName_Readdress.cs b/src/AddressRegistry/StreetName/StreetName_Readdress.cs index bbd70d661..36787741c 100644 --- a/src/AddressRegistry/StreetName/StreetName_Readdress.cs +++ b/src/AddressRegistry/StreetName/StreetName_Readdress.cs @@ -3,7 +3,6 @@ namespace AddressRegistry.StreetName using System; using System.Collections.Generic; using System.Linq; - using Address; using Commands; using DataStructures; using Events; @@ -46,11 +45,11 @@ public void Readdress( readressedData.readdressedBoxNumbers)); } - // Reject Retire Actions - var rejectRetireActions = streetNameReaddresser.Actions.Where(x => - x.action == ReaddressAction.Reject || x.action == ReaddressAction.Retire); + // Reject or Retire BoxNumber Actions + var rejectRetireBoxNumberActions = streetNameReaddresser.Actions.Where(x => + x.action is ReaddressAction.RejectBoxNumber or ReaddressAction.RetireBoxNumber); - foreach (var (action, addressPersistentLocalId) in rejectRetireActions) + foreach (var (action, addressPersistentLocalId) in rejectRetireBoxNumberActions) { HandleAction( action, @@ -117,11 +116,11 @@ private void HandleAction( executionContext.AddressesAdded.Add((PersistentLocalId, addressPersistentLocalId)); break; - case ReaddressAction.Reject: + case ReaddressAction.RejectBoxNumber: RejectAddressBecauseOfReaddress(addressPersistentLocalId); break; - case ReaddressAction.Retire: + case ReaddressAction.RetireBoxNumber: RetireAddressBecauseOfReaddress(addressPersistentLocalId); break; diff --git a/test/AddressRegistry.Tests/AggregateTests/WhenReaddress/GivenThreeAddresses/FirstAndSecondAddressesHaveBoxNumbers.cs b/test/AddressRegistry.Tests/AggregateTests/WhenReaddress/GivenThreeAddresses/FirstAndSecondAddressesHaveBoxNumbers.cs index d4ba3ae25..43f423fc7 100644 --- a/test/AddressRegistry.Tests/AggregateTests/WhenReaddress/GivenThreeAddresses/FirstAndSecondAddressesHaveBoxNumbers.cs +++ b/test/AddressRegistry.Tests/AggregateTests/WhenReaddress/GivenThreeAddresses/FirstAndSecondAddressesHaveBoxNumbers.cs @@ -1,18 +1,18 @@ namespace AddressRegistry.Tests.AggregateTests.WhenReaddress.GivenThreeAddresses { using System.Collections.Generic; - using AddressRegistry.Api.BackOffice.Abstractions; - using AddressRegistry.StreetName; - using AddressRegistry.StreetName.Commands; - using AddressRegistry.StreetName.DataStructures; - using AddressRegistry.StreetName.Events; - using AddressRegistry.Tests.AutoFixture; + using Api.BackOffice.Abstractions; + using AutoFixture; using Be.Vlaanderen.Basisregisters.AggregateSource; using Be.Vlaanderen.Basisregisters.AggregateSource.Testing; using Be.Vlaanderen.Basisregisters.GrAr.Provenance; using EventBuilders; using FluentAssertions; using global::AutoFixture; + using StreetName; + using StreetName.Commands; + using StreetName.DataStructures; + using StreetName.Events; using Xunit; using Xunit.Abstractions; @@ -301,5 +301,240 @@ public void ThenBoxNumbersAreReadressed() x.streetNamePersistentLocalId == _streetNamePersistentLocalId && x.addressPersistentLocalId == expectedThirdAddressPersistentLocalId); } + + [Fact] + public void WithSecondAddressOtherBoxNumbers_ThenDifferingBoxNumbersWereProposedAndRejectedOrRetired() + { + var firstAddressPersistentLocalId = new AddressPersistentLocalId(40001932); + + var secondAddressPersistentLocalId = new AddressPersistentLocalId(40003037); + var secondAddressProposedBoxNumberPersistentLocalId = new AddressPersistentLocalId(40003043); + var secondAddressCurrentBoxNumberPersistentLocalId = new AddressPersistentLocalId(40003044); + + var thirdAddressPersistentLocalId = new AddressPersistentLocalId(40003039); + var thirdAddressBoxNumberPersistentLocalId = new AddressPersistentLocalId(40003046); + + var firstHouseNumber = new HouseNumber("3"); + var secondHouseNumber = new HouseNumber("6"); + var thirdHouseNumber = new HouseNumber("7"); + + var postalCode = Fixture.Create(); + + var firstAddressWasMigrated = new AddressWasMigratedToStreetNameBuilder(Fixture, AddressStatus.Current) + .WithAddressPersistentLocalId(firstAddressPersistentLocalId) + .WithHouseNumber(firstHouseNumber) + .WithAddressGeometry(new AddressGeometry( + GeometryMethod.AppointedByAdministrator, + GeometrySpecification.Entry, + GeometryHelpers.ThirdGmlPointGeometry.ToExtendedWkbGeometry())) + .WithPostalCode(postalCode) + .Build(); + + var secondAddressWasMigrated = new AddressWasMigratedToStreetNameBuilder(Fixture, AddressStatus.Current) + .WithAddressPersistentLocalId(secondAddressPersistentLocalId) + .WithHouseNumber(secondHouseNumber) + .WithAddressGeometry(new AddressGeometry( + GeometryMethod.AppointedByAdministrator, + GeometrySpecification.Entry, + GeometryHelpers.GmlPointGeometry.ToExtendedWkbGeometry())) + .WithPostalCode(postalCode) + .Build(); + + var secondProposedBoxNumberAddressWasMigrated = + new AddressWasMigratedToStreetNameBuilder(Fixture, AddressStatus.Proposed) + .WithAddressPersistentLocalId(secondAddressProposedBoxNumberPersistentLocalId) + .WithHouseNumber(secondHouseNumber) + .WithBoxNumber(new BoxNumber("A"), secondAddressPersistentLocalId) + .WithAddressGeometry(new AddressGeometry( + GeometryMethod.AppointedByAdministrator, + GeometrySpecification.Entry, + GeometryHelpers.GmlPointGeometry.ToExtendedWkbGeometry())) + .Build(); + + var secondCurrentBoxNumberAddressWasMigrated = + new AddressWasMigratedToStreetNameBuilder(Fixture, AddressStatus.Current) + .WithAddressPersistentLocalId(secondAddressCurrentBoxNumberPersistentLocalId) + .WithHouseNumber(secondHouseNumber) + .WithBoxNumber(new BoxNumber("B"), secondAddressPersistentLocalId) + .WithAddressGeometry(new AddressGeometry( + GeometryMethod.AppointedByAdministrator, + GeometrySpecification.Entry, + GeometryHelpers.GmlPointGeometry.ToExtendedWkbGeometry())) + .Build(); + + var thirdAddressWasMigrated = new AddressWasMigratedToStreetNameBuilder(Fixture, AddressStatus.Current) + .WithAddressPersistentLocalId(thirdAddressPersistentLocalId) + .WithHouseNumber(thirdHouseNumber) + .WithAddressGeometry(new AddressGeometry( + GeometryMethod.AppointedByAdministrator, + GeometrySpecification.Entry, + GeometryHelpers.SecondGmlPointGeometry.ToExtendedWkbGeometry())) + .WithPostalCode(postalCode) + .Build(); + + var thirdBoxNumberAddressWasMigrated = + new AddressWasMigratedToStreetNameBuilder(Fixture, AddressStatus.Proposed) + .WithAddressPersistentLocalId(thirdAddressBoxNumberPersistentLocalId) + .WithHouseNumber(thirdHouseNumber) + .WithBoxNumber(new BoxNumber("A"), thirdAddressPersistentLocalId) + .WithAddressGeometry(new AddressGeometry( + GeometryMethod.AppointedByAdministrator, + GeometrySpecification.Entry, + GeometryHelpers.SecondGmlPointGeometry.ToExtendedWkbGeometry())) + .Build(); + + var command = new Readdress( + _streetNamePersistentLocalId, + new List + { + new ReaddressAddressItem(_streetNamePersistentLocalId, secondAddressPersistentLocalId, + firstHouseNumber), + new ReaddressAddressItem(_streetNamePersistentLocalId, thirdAddressPersistentLocalId, + secondHouseNumber) + }, + new List(), + Fixture.Create()); + + var expectedProposedBoxNumberAddressPersistentLocalId = + new AddressPersistentLocalId(1); // FakePersistentLocalIdGenerator starts with id 1 + var expectedCurrentBoxNumberAddressPersistentLocalId = new AddressPersistentLocalId(2); + + Assert(new Scenario() + .Given(_streamId, + Fixture.Create(), + firstAddressWasMigrated, + secondAddressWasMigrated, + secondProposedBoxNumberAddressWasMigrated, + secondCurrentBoxNumberAddressWasMigrated, + thirdAddressWasMigrated, + thirdBoxNumberAddressWasMigrated) + .When(command) + .Then(new[] + { + new Fact(_streamId, + new AddressWasProposedBecauseOfReaddress( + _streetNamePersistentLocalId, + expectedProposedBoxNumberAddressPersistentLocalId, + secondAddressProposedBoxNumberPersistentLocalId, + firstAddressPersistentLocalId, + new PostalCode(secondAddressWasMigrated.PostalCode!), + firstHouseNumber, + new BoxNumber(secondProposedBoxNumberAddressWasMigrated.BoxNumber!), + secondProposedBoxNumberAddressWasMigrated.GeometryMethod, + secondProposedBoxNumberAddressWasMigrated.GeometrySpecification, + new ExtendedWkbGeometry(secondProposedBoxNumberAddressWasMigrated.ExtendedWkbGeometry))), + new Fact(_streamId, + new AddressWasProposedBecauseOfReaddress( + _streetNamePersistentLocalId, + expectedCurrentBoxNumberAddressPersistentLocalId, + secondAddressCurrentBoxNumberPersistentLocalId, + firstAddressPersistentLocalId, + new PostalCode(secondAddressWasMigrated.PostalCode!), + firstHouseNumber, + new BoxNumber(secondCurrentBoxNumberAddressWasMigrated.BoxNumber!), + secondCurrentBoxNumberAddressWasMigrated.GeometryMethod, + secondCurrentBoxNumberAddressWasMigrated.GeometrySpecification, + new ExtendedWkbGeometry(secondCurrentBoxNumberAddressWasMigrated.ExtendedWkbGeometry))), + new Fact(_streamId, + new AddressHouseNumberWasReaddressed( + _streetNamePersistentLocalId, + firstAddressPersistentLocalId, + readdressedHouseNumber: new ReaddressedAddressData( + secondAddressPersistentLocalId, + firstAddressPersistentLocalId, + isDestinationNewlyProposed: false, + secondAddressWasMigrated.Status, + firstHouseNumber, + boxNumber: null, + new PostalCode(secondAddressWasMigrated.PostalCode!), + new AddressGeometry( + secondAddressWasMigrated.GeometryMethod, + secondAddressWasMigrated.GeometrySpecification, + new ExtendedWkbGeometry(secondAddressWasMigrated.ExtendedWkbGeometry)), + secondAddressWasMigrated.OfficiallyAssigned), + readdressedBoxNumbers: new List + { + new ReaddressedAddressData( + secondAddressProposedBoxNumberPersistentLocalId, + expectedProposedBoxNumberAddressPersistentLocalId, + isDestinationNewlyProposed: true, + secondProposedBoxNumberAddressWasMigrated.Status, + firstHouseNumber, + new BoxNumber(secondProposedBoxNumberAddressWasMigrated.BoxNumber!), + new PostalCode(secondAddressWasMigrated.PostalCode!), + new AddressGeometry( + secondProposedBoxNumberAddressWasMigrated.GeometryMethod, + secondProposedBoxNumberAddressWasMigrated.GeometrySpecification, + new ExtendedWkbGeometry(secondProposedBoxNumberAddressWasMigrated.ExtendedWkbGeometry)), + secondProposedBoxNumberAddressWasMigrated.OfficiallyAssigned), + new ReaddressedAddressData( + secondAddressCurrentBoxNumberPersistentLocalId, + expectedCurrentBoxNumberAddressPersistentLocalId, + isDestinationNewlyProposed: true, + secondCurrentBoxNumberAddressWasMigrated.Status, + firstHouseNumber, + new BoxNumber(secondCurrentBoxNumberAddressWasMigrated.BoxNumber!), + new PostalCode(secondAddressWasMigrated.PostalCode!), + new AddressGeometry( + secondCurrentBoxNumberAddressWasMigrated.GeometryMethod, + secondCurrentBoxNumberAddressWasMigrated.GeometrySpecification, + new ExtendedWkbGeometry(secondCurrentBoxNumberAddressWasMigrated.ExtendedWkbGeometry)), + secondCurrentBoxNumberAddressWasMigrated.OfficiallyAssigned) + })), + new Fact(_streamId, + new AddressHouseNumberWasReaddressed( + _streetNamePersistentLocalId, + secondAddressPersistentLocalId, + readdressedHouseNumber: new ReaddressedAddressData( + thirdAddressPersistentLocalId, + secondAddressPersistentLocalId, + isDestinationNewlyProposed: false, + thirdAddressWasMigrated.Status, + secondHouseNumber, + boxNumber: null, + new PostalCode(thirdAddressWasMigrated.PostalCode!), + new AddressGeometry( + thirdAddressWasMigrated.GeometryMethod, + thirdAddressWasMigrated.GeometrySpecification, + new ExtendedWkbGeometry(thirdAddressWasMigrated.ExtendedWkbGeometry)), + thirdAddressWasMigrated.OfficiallyAssigned), + readdressedBoxNumbers: new List + { + new ReaddressedAddressData( + thirdAddressBoxNumberPersistentLocalId, + secondAddressProposedBoxNumberPersistentLocalId, + isDestinationNewlyProposed: false, + thirdBoxNumberAddressWasMigrated.Status, + secondHouseNumber, + new BoxNumber(thirdBoxNumberAddressWasMigrated.BoxNumber!), + new PostalCode(thirdAddressWasMigrated.PostalCode!), + new AddressGeometry( + thirdBoxNumberAddressWasMigrated.GeometryMethod, + thirdBoxNumberAddressWasMigrated.GeometrySpecification, + new ExtendedWkbGeometry(thirdBoxNumberAddressWasMigrated.ExtendedWkbGeometry)), + thirdBoxNumberAddressWasMigrated.OfficiallyAssigned) + })), + new Fact(_streamId, + new AddressWasRetiredBecauseOfReaddress( + _streetNamePersistentLocalId, + secondAddressCurrentBoxNumberPersistentLocalId)) + })); + + command.ExecutionContext.AddressesAdded.Should().HaveCount(2); + command.ExecutionContext.AddressesAdded.Should().ContainSingle(x => + x.streetNamePersistentLocalId == _streetNamePersistentLocalId + && x.addressPersistentLocalId == expectedProposedBoxNumberAddressPersistentLocalId); + command.ExecutionContext.AddressesAdded.Should().ContainSingle(x => + x.streetNamePersistentLocalId == _streetNamePersistentLocalId + && x.addressPersistentLocalId == expectedCurrentBoxNumberAddressPersistentLocalId); + + command.ExecutionContext.AddressesUpdated.Should().HaveCount(2); + command.ExecutionContext.AddressesUpdated.Should().ContainSingle(x => + x.streetNamePersistentLocalId == _streetNamePersistentLocalId + && x.addressPersistentLocalId == secondAddressPersistentLocalId); + command.ExecutionContext.AddressesUpdated.Should().ContainSingle(x => + x.streetNamePersistentLocalId == _streetNamePersistentLocalId + && x.addressPersistentLocalId == firstAddressPersistentLocalId); + } } }