diff --git a/java/google/registry/model/billing/BillingEvent.java b/java/google/registry/model/billing/BillingEvent.java index d55f88168..5a87bce4f 100644 --- a/java/google/registry/model/billing/BillingEvent.java +++ b/java/google/registry/model/billing/BillingEvent.java @@ -225,6 +225,13 @@ public abstract class BillingEvent extends ImmutableObject @Index DateTime syntheticCreationTime; + /** + * For {@link Flag#SYNTHETIC} events, the {@link BillingEvent} from which this OneTime was + * created. This is needed in order to properly match billing events against + * {@link Cancellation}s. + */ + Long cancellationTargetId; + public Money getCost() { return cost; } @@ -241,6 +248,10 @@ public abstract class BillingEvent extends ImmutableObject return syntheticCreationTime; } + public Long getCancellationTargetId() { + return cancellationTargetId; + } + @Override public Builder asBuilder() { return new Builder(clone(this)); @@ -277,6 +288,11 @@ public abstract class BillingEvent extends ImmutableObject return this; } + public Builder setCancellationTargetId(Long cancellationTargetId) { + getInstance().cancellationTargetId = cancellationTargetId; + return this; + } + @Override public OneTime build() { OneTime instance = getInstance(); @@ -291,7 +307,11 @@ public abstract class BillingEvent extends ImmutableObject checkState( instance.getFlags().contains(Flag.SYNTHETIC) == (instance.syntheticCreationTime != null), - "Billing events with SYNTHETIC flag set must have a synthetic creation time."); + "Synthetic creation time must be set if and only if the SYNTHETIC flag is set."); + checkState( + instance.getFlags().contains(Flag.SYNTHETIC) + == (instance.cancellationTargetId != null), + "Cancellation target ID must be set if and only if the SYNTHETIC flag is set."); return super.build(); } } diff --git a/javatests/google/registry/model/billing/BillingEventTest.java b/javatests/google/registry/model/billing/BillingEventTest.java index a067dd50e..abfb5a9fa 100644 --- a/javatests/google/registry/model/billing/BillingEventTest.java +++ b/javatests/google/registry/model/billing/BillingEventTest.java @@ -91,6 +91,7 @@ public class BillingEventTest extends EntityTestCase { .setReason(Reason.CREATE) .setFlags(ImmutableSet.of(BillingEvent.Flag.ANCHOR_TENANT, BillingEvent.Flag.SYNTHETIC)) .setSyntheticCreationTime(now.plusDays(10)) + .setCancellationTargetId(1L) .setPeriodYears(2) .setCost(Money.of(USD, 1)) .setEventTime(now) @@ -179,7 +180,7 @@ public class BillingEventTest extends EntityTestCase { public void testFailure_syntheticFlagWithoutCreationTime() { thrown.expect( IllegalStateException.class, - "Billing events with SYNTHETIC flag set must have a synthetic creation time"); + "Synthetic creation time must be set if and only if the SYNTHETIC flag is set."); oneTime.asBuilder() .setFlags(ImmutableSet.of(BillingEvent.Flag.SYNTHETIC)) .build(); @@ -189,12 +190,32 @@ public class BillingEventTest extends EntityTestCase { public void testFailure_syntheticCreationTimeWithoutFlag() { thrown.expect( IllegalStateException.class, - "Billing events with SYNTHETIC flag set must have a synthetic creation time"); + "Synthetic creation time must be set if and only if the SYNTHETIC flag is set"); oneTime.asBuilder() .setSyntheticCreationTime(now.plusDays(10)) .build(); } + @Test + public void testFailure_syntheticFlagWithoutCancellationTargetId() { + thrown.expect( + IllegalStateException.class, + "Synthetic creation time must be set if and only if the SYNTHETIC flag is set."); + oneTime.asBuilder() + .setFlags(ImmutableSet.of(BillingEvent.Flag.SYNTHETIC)) + .build(); + } + + @Test + public void testFailure_cancellationTargetIdWithoutFlag() { + thrown.expect( + IllegalStateException.class, + "Cancellation target ID must be set if and only if the SYNTHETIC flag is set"); + oneTime.asBuilder() + .setCancellationTargetId(2L) + .build(); + } + @Test public void testSuccess_cancellation_forGracePeriod_withOneTime() { BillingEvent.Cancellation newCancellation = BillingEvent.Cancellation.forGracePeriod(