Add a renewal cost for ATs when renewal is SPECIFIED (#2484)

Note: this is not used yet
This commit is contained in:
gbrodman
2024-07-09 18:39:48 +00:00
committed by GitHub
parent b8a6ac72dd
commit 092e3dca47
21 changed files with 3733 additions and 3467 deletions
@@ -50,6 +50,7 @@ import google.registry.model.domain.fee.FeeQueryCommandExtensionItem.CommandName
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.VKey;
import google.registry.persistence.WithVKey;
import google.registry.persistence.converter.JodaMoneyType;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -63,6 +64,9 @@ import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.Table;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Type;
import org.joda.money.Money;
import org.joda.time.DateTime;
/** An entity representing an allocation token. */
@@ -236,6 +240,12 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
@Column(name = "renewalPriceBehavior", nullable = false)
RenewalPriceBehavior renewalPriceBehavior = RenewalPriceBehavior.DEFAULT;
/** The price used for renewals iff the renewalPriceBehavior is SPECIFIED. */
@Nullable
@Type(type = JodaMoneyType.TYPE_NAME)
@Columns(columns = {@Column(name = "renewalPriceAmount"), @Column(name = "renewalPriceCurrency")})
Money renewalPrice;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
RegistrationBehavior registrationBehavior = RegistrationBehavior.DEFAULT;
@@ -310,6 +320,10 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
return renewalPriceBehavior;
}
public Optional<Money> getRenewalPrice() {
return Optional.ofNullable(renewalPrice);
}
public RegistrationBehavior getRegistrationBehavior() {
return registrationBehavior;
}
@@ -377,29 +391,12 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
public AllocationToken build() {
checkArgumentNotNull(getInstance().tokenType, "Token type must be specified");
checkArgument(!Strings.isNullOrEmpty(getInstance().token), "Token must not be null or empty");
checkArgument(
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|| getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED),
"Bulk tokens must have renewalPriceBehavior set to SPECIFIED");
checkArgument(
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|| ImmutableSet.of(CommandName.CREATE).equals(getInstance().allowedEppActions),
"Bulk tokens may only be valid for CREATE actions");
checkArgument(
!getInstance().tokenType.equals(TokenType.BULK_PRICING)
|| !getInstance().discountPremiums,
"Bulk tokens cannot discount premium names");
checkArgument(
getInstance().domainName == null || getInstance().tokenType.isOneTimeUse(),
"Domain name can only be specified for SINGLE_USE or REGISTER_BSA tokens");
checkArgument(
getInstance().redemptionHistoryId == null || getInstance().tokenType.isOneTimeUse(),
"Redemption history entry can only be specified for SINGLE_USE or REGISTER_BSA tokens");
checkArgument(
getInstance().tokenType != TokenType.BULK_PRICING
|| (getInstance().allowedClientIds != null
&& getInstance().allowedClientIds.size() == 1),
"BULK_PRICING tokens must have exactly one allowed client registrar");
checkArgument(
getInstance().discountFraction > 0 || !getInstance().discountPremiums,
"Discount premiums can only be specified along with a discount fraction");
@@ -425,6 +422,33 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|| getInstance().allowedEppActions.contains(CommandName.CREATE),
"NONPREMIUM_CREATE tokens must allow for CREATE actions");
}
checkArgument(
getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED)
== (getInstance().renewalPrice != null),
"renewalPrice must be specified iff renewalPriceBehavior is SPECIFIED");
if (getInstance().tokenType.equals(TokenType.BULK_PRICING)) {
checkArgument(
getInstance().discountFraction == 1.0,
"BULK_PRICING tokens must have a discountFraction of 1.0");
checkArgument(
!getInstance().shouldDiscountPremiums(),
"BULK_PRICING tokens cannot discount premium names");
checkArgument(
getInstance().renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED),
"BULK_PRICING tokens must have renewalPriceBehavior set to SPECIFIED");
checkArgument(
getInstance().renewalPrice.getAmount().intValue() == 0,
"BULK_PRICING tokens must have a renewal price of 0");
checkArgument(
ImmutableSet.of(CommandName.CREATE).equals(getInstance().allowedEppActions),
"BULK_PRICING tokens may only be valid for CREATE actions");
checkArgument(
getInstance().allowedClientIds != null && getInstance().allowedClientIds.size() == 1,
"BULK_PRICING tokens must have exactly one allowed client registrar");
}
if (getInstance().domainName != null) {
try {
DomainFlowUtils.validateDomainName(getInstance().domainName);
@@ -521,6 +545,11 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
return this;
}
public Builder setRenewalPrice(Money renewalPrice) {
getInstance().renewalPrice = renewalPrice;
return this;
}
public Builder setRegistrationBehavior(RegistrationBehavior registrationBehavior) {
getInstance().registrationBehavior = registrationBehavior;
return this;
@@ -30,6 +30,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.beust.jcommander.internal.Nullable;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
@@ -62,6 +63,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Named;
import org.joda.money.Money;
import org.joda.time.DateTime;
/** Command to generate and persist {@link AllocationToken}s. */
@@ -158,10 +160,16 @@ class GenerateAllocationTokensCommand implements Command {
"The type of renewal price behavior, either DEFAULT (default), NONPREMIUM, or SPECIFIED."
+ " This indicates how a domain should be charged for renewal. By default, a domain"
+ " will be renewed at the renewal price from the pricing engine. If the renewal"
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be the"
+ " same as the domain's calculated create price.")
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be equal"
+ " to the provided renewal price.")
private RenewalPriceBehavior renewalPriceBehavior = DEFAULT;
@Parameter(
names = {"--renewal_price"},
description = "The renewal price amount iff the renewal price behavior is SPECIFIED.")
@Nullable
private Money renewalPrice;
@Parameter(
names = {"--registration_behavior"},
description =
@@ -228,6 +236,7 @@ class GenerateAllocationTokensCommand implements Command {
Optional.ofNullable(discountYears).ifPresent(token::setDiscountYears);
Optional.ofNullable(tokenStatusTransitions)
.ifPresent(token::setTokenStatusTransitions);
Optional.ofNullable(renewalPrice).ifPresent(token::setRenewalPrice);
Optional.ofNullable(domainNames)
.ifPresent(d -> token.setDomainName(d.removeFirst()));
return token.build();
@@ -295,6 +304,10 @@ class GenerateAllocationTokensCommand implements Command {
+ " map");
}
checkArgument(
renewalPriceBehavior.equals(RenewalPriceBehavior.SPECIFIED) == (renewalPrice != null),
"renewal_price must be specified iff renewal_price_behavior is SPECIFIED");
if (tokenStrings != null) {
verifyTokenStringsDoNotExist();
}
@@ -40,6 +40,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import org.joda.money.Money;
import org.joda.time.DateTime;
/** Command to update existing {@link AllocationToken}s. */
@@ -110,11 +111,17 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
"The type of renewal price behavior, either DEFAULT (default), NONPREMIUM, or SPECIFIED."
+ " This indicates how a domain should be charged for renewal. By default, a domain"
+ " will be renewed at the renewal price from the pricing engine. If the renewal"
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be the"
+ " same as the domain's calculated create price.")
+ " price behavior is set to SPECIFIED, it means that the renewal cost will be equal"
+ " to the provided renewal price.")
@Nullable
private RenewalPriceBehavior renewalPriceBehavior;
@Parameter(
names = {"--renewal_price"},
description = "The renewal price amount iff the renewal price behavior is SPECIFIED.")
@Nullable
private Money renewalPrice;
@Parameter(
names = {"--registration_behavior"},
description =
@@ -189,17 +196,22 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
.ifPresent(tlds -> builder.setAllowedTlds(ImmutableSet.copyOf(tlds)));
Optional.ofNullable(allowedEppActions)
.ifPresent(
eppActions -> {
builder.setAllowedEppActions(
eppActions.stream()
.map(CommandName::parseKnownCommand)
.collect(toImmutableSet()));
});
eppActions ->
builder.setAllowedEppActions(
eppActions.stream()
.map(CommandName::parseKnownCommand)
.collect(toImmutableSet())));
Optional.ofNullable(discountFraction).ifPresent(builder::setDiscountFraction);
Optional.ofNullable(discountPremiums).ifPresent(builder::setDiscountPremiums);
Optional.ofNullable(discountYears).ifPresent(builder::setDiscountYears);
Optional.ofNullable(tokenStatusTransitions).ifPresent(builder::setTokenStatusTransitions);
if (renewalPriceBehavior != null
&& renewalPriceBehavior != original.getRenewalPriceBehavior()) {
builder.setRenewalPrice(null);
}
Optional.ofNullable(renewalPriceBehavior).ifPresent(builder::setRenewalPriceBehavior);
Optional.ofNullable(renewalPrice).ifPresent(builder::setRenewalPrice);
Optional.ofNullable(registrationBehavior).ifPresent(builder::setRegistrationBehavior);
return builder.build();
}