diff --git a/docs/architecture.md b/docs/architecture.md
index 3d40707ce..c55587225 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -377,11 +377,6 @@ registry codebase:
* `Registrar` -- These hold information about client registrars.
* `RegistrarContact` -- Registrars have contacts just as domains do. These are
stored in a special RegistrarContact entity.
-* `RegistrarCredit` and `RegistrarCreditBalance` -- These entities implement
- the concept of registrar "credits" which are pools of credit that would
- apply to charges owed by the registrar. Note, however, that Nomulus
- currently has no support for actually incorporating credits into billing
- output, and these entities may be removed at any time.
* `Registry` -- These hold information about the TLDs supported by the
Registry system.
* `RegistryCursor` -- These entities are the predecessor to the Cursor
diff --git a/java/google/registry/model/EntityClasses.java b/java/google/registry/model/EntityClasses.java
index c5a08fbfb..e599157ca 100644
--- a/java/google/registry/model/EntityClasses.java
+++ b/java/google/registry/model/EntityClasses.java
@@ -16,9 +16,6 @@ package google.registry.model;
import com.google.common.collect.ImmutableSet;
import google.registry.model.billing.BillingEvent;
-import google.registry.model.billing.RegistrarBillingEntry;
-import google.registry.model.billing.RegistrarCredit;
-import google.registry.model.billing.RegistrarCreditBalance;
import google.registry.model.common.Cursor;
import google.registry.model.common.EntityGroupRoot;
import google.registry.model.common.GaeUserIdConverter;
@@ -103,10 +100,7 @@ public final class EntityClasses {
PremiumList.PremiumListRevision.class,
RdeRevision.class,
Registrar.class,
- RegistrarBillingEntry.class,
RegistrarContact.class,
- RegistrarCredit.class,
- RegistrarCreditBalance.class,
Registry.class,
ReservedList.class,
ServerSecret.class,
diff --git a/java/google/registry/model/billing/RegistrarBillingEntry.java b/java/google/registry/model/billing/RegistrarBillingEntry.java
deleted file mode 100644
index e1baf34a1..000000000
--- a/java/google/registry/model/billing/RegistrarBillingEntry.java
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.model.billing;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.base.Strings.emptyToNull;
-import static com.google.common.base.Verify.verifyNotNull;
-
-import com.googlecode.objectify.Key;
-import com.googlecode.objectify.annotation.Entity;
-import com.googlecode.objectify.annotation.Id;
-import com.googlecode.objectify.annotation.Index;
-import com.googlecode.objectify.annotation.Parent;
-import google.registry.model.Buildable;
-import google.registry.model.ImmutableObject;
-import google.registry.model.JsonMapBuilder;
-import google.registry.model.Jsonifiable;
-import google.registry.model.registrar.Registrar;
-import java.util.Map;
-import javax.annotation.Nullable;
-import org.joda.money.CurrencyUnit;
-import org.joda.money.Money;
-import org.joda.time.DateTime;
-
-/**
- * Log of monthly invoices and payments for a Registrar customer.
- *
- *
This is a one-off single-entry bookkeeping system. There is a separate account for each
- * (registrar, currency) pair.
- *
- *
You should never update these entities once they've been inserted into Datastore. If you need
- * to change something, add a correction entry.
- */
-@Entity
-public class RegistrarBillingEntry extends ImmutableObject implements Jsonifiable {
-
- @Parent
- Key parent;
-
- /** Arbitrary unique identifier. */
- @Id
- long id;
-
- /**
- * External transaction identifier or {@code null} if this is an invoice entry.
- *
- *
This is the ID or token that the payment gateway gives us, which represents the transaction
- * in their database.
- */
- @Nullable
- String transactionId;
-
- /**
- * Time at which this entry was created.
- *
- *
This value is unique and monotonic for a given ({@link #parent}, {@link #currency}) pair.
- */
- @Index
- DateTime created;
-
- /** Completely arbitrary description of payment. */
- String description;
-
- /**
- * Currency of transaction.
- *
- *
This field is identical to {@code amount.getCurrencyUnit()} and is only here so it can be
- * indexed in Datastore.
- */
- @Index
- CurrencyUnit currency;
-
- /**
- * Amount and currency of invoice or payment.
- *
- *
This field is positive for debits (e.g. monthly invoice entries) and negative for credits
- * (e.g. credit card payment transaction entries.)
- */
- Money amount;
-
- /**
- * Balance of account for this currency.
- *
- *
This is {@code amount + previous.balance}.
- */
- Money balance;
-
- public Key getParent() {
- return parent;
- }
-
- public long getId() {
- return id;
- }
-
- @Nullable
- public String getTransactionId() {
- return transactionId;
- }
-
- public DateTime getCreated() {
- return verifyNotNull(created, "created missing: %s", this);
- }
-
- public String getDescription() {
- return verifyNotNull(description, "description missing: %s", this);
- }
-
- public CurrencyUnit getCurrency() {
- return verifyNotNull(currency, "currency missing: %s", this);
- }
-
- public Money getAmount() {
- return verifyNotNull(amount, "amount missing: %s", this);
- }
-
- public Money getBalance() {
- return verifyNotNull(balance, "balance missing: %s", this);
- }
-
- @Override
- public Map toJsonMap() {
- return new JsonMapBuilder()
- .put("id", id)
- .put("transactionId", getTransactionId())
- .putString("created", getCreated())
- .put("description", getDescription())
- .putString("currency", getCurrency())
- .putString("amount", getAmount().getAmount())
- .putString("balance", getBalance().getAmount())
- .build();
- }
-
- /** A builder for constructing a {@link RegistrarBillingEntry}, since it's immutable. */
- public static class Builder extends Buildable.Builder {
-
- @Nullable
- private RegistrarBillingEntry previous;
-
- public Builder setParent(Registrar parent) {
- getInstance().parent = Key.create(parent);
- return this;
- }
-
- public Builder setCreated(DateTime created) {
- getInstance().created = created;
- return this;
- }
-
- public Builder setPrevious(@Nullable RegistrarBillingEntry previous) {
- this.previous = previous;
- return this;
- }
-
- public Builder setTransactionId(@Nullable String transactionId) {
- getInstance().transactionId = transactionId;
- return this;
- }
-
- public Builder setDescription(String description) {
- getInstance().description = checkNotNull(emptyToNull(description));
- return this;
- }
-
- public Builder setAmount(Money amount) {
- checkArgument(!amount.isZero(), "Amount can't be zero");
- getInstance().amount = amount;
- getInstance().currency = amount.getCurrencyUnit();
- return this;
- }
-
- @Override
- public RegistrarBillingEntry build() {
- checkNotNull(getInstance().parent, "parent");
- checkNotNull(getInstance().created, "created");
- checkNotNull(getInstance().description, "description");
- checkNotNull(getInstance().amount, "amount");
- if (previous == null) {
- getInstance().balance = getInstance().amount;
- } else {
- getInstance().balance = previous.balance.plus(getInstance().amount);
- checkState(getInstance().parent.equals(previous.parent),
- "Parent not same as previous:\nNew: %s\nPrevious: %s",
- getInstance(), previous);
- checkState(getInstance().created.isAfter(previous.created),
- "Created timestamp not after previous:\nNew: %s\nPrevious: %s",
- getInstance(), previous);
- }
- return cloneEmptyToNull(super.build());
- }
- }
-}
diff --git a/java/google/registry/model/billing/RegistrarBillingUtils.java b/java/google/registry/model/billing/RegistrarBillingUtils.java
deleted file mode 100644
index 0e8d22400..000000000
--- a/java/google/registry/model/billing/RegistrarBillingUtils.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.model.billing;
-
-import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
-import static google.registry.model.ofy.ObjectifyService.ofy;
-
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Ordering;
-import com.googlecode.objectify.cmd.Query;
-import google.registry.model.CacheUtils;
-import google.registry.model.registrar.Registrar;
-import google.registry.model.registry.Registries;
-import google.registry.model.registry.Registry;
-import java.util.Map;
-import org.joda.money.CurrencyUnit;
-import org.joda.money.Money;
-
-/** Utilities for managing the billing of {@link Registrar} customers. */
-public final class RegistrarBillingUtils {
-
- private static final Supplier> CURRENCIES_CACHE =
- CacheUtils.memoizeWithShortExpiration(
- () ->
- Registries.getTlds()
- .stream()
- .map((String tld) -> Registry.get(tld).getCurrency())
- .collect(toImmutableSortedSet(Ordering.natural())));
-
- /**
- * Returns set of currencies in which registrars may be billed.
- *
- *
Each TLD has a currency associated with it. We don't do conversions. The registrar customer
- * gets a separate bill for each currency.
- */
- public static ImmutableSortedSet getCurrencies() {
- return CURRENCIES_CACHE.get();
- }
-
- /**
- * Returns query of {@link RegistrarBillingEntry} for each currency, most recent first.
- *
- *
Note: Currency map keys are returned in sorted order, from {@link #getCurrencies()}.
- */
- public static ImmutableMap> getBillingEntryQueries(
- final Registrar registrar) {
- return Maps.toMap(
- getCurrencies(),
- (CurrencyUnit currency) ->
- ofy()
- .load()
- .type(RegistrarBillingEntry.class)
- .ancestor(registrar)
- .filter("currency", currency)
- .order("-created"));
- }
-
- /** Returns amount of money registrar currently owes registry in each currency. */
- public static Map loadBalance(Registrar registrar) {
- return Maps.transformEntries(
- getBillingEntryQueries(registrar),
- (CurrencyUnit currency, Query query) -> {
- RegistrarBillingEntry entry = query.first().now();
- return entry != null ? entry.getBalance() : Money.zero(currency);
- });
- }
-
- private RegistrarBillingUtils() {}
-}
diff --git a/java/google/registry/model/billing/RegistrarCredit.java b/java/google/registry/model/billing/RegistrarCredit.java
deleted file mode 100644
index 0f8d8fc4a..000000000
--- a/java/google/registry/model/billing/RegistrarCredit.java
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.model.billing;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static google.registry.model.ofy.ObjectifyService.ofy;
-import static google.registry.model.registry.Registries.assertTldExists;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ComparisonChain;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Streams;
-import com.googlecode.objectify.Key;
-import com.googlecode.objectify.annotation.Entity;
-import com.googlecode.objectify.annotation.Id;
-import com.googlecode.objectify.annotation.Parent;
-import google.registry.model.Buildable;
-import google.registry.model.ImmutableObject;
-import google.registry.model.annotations.ReportedOn;
-import google.registry.model.registrar.Registrar;
-import google.registry.model.registry.Registry;
-import java.util.Optional;
-import org.joda.money.CurrencyUnit;
-import org.joda.time.DateTime;
-
-/**
- * A per-registrar billing credit, applied toward future charges for registrar activity.
- *
- *
NOTE: While credits are tracked within the model, there is no built-in support for actually
- * incorporating these credits into the generated billing data, and the model support for credits
- * should be considered quasi-deprecated (it may be removed without notice).
- */
-@ReportedOn
-@Entity
-public final class RegistrarCredit extends ImmutableObject implements Buildable {
-
- /**
- * The type of credit represented. The ordering below determines the order in which credits of
- * of different types will be applied to an invoice charge.
- */
- // Note: Right now the ordering is actually maintained manually via a hard-coded table in the
- // relevant billing query, so if adding a credit type here, add it there as well.
- // TODO(b/19031546): make the query automatically reflect the order in this enum.
- public enum CreditType {
- /** Credit awarded as an incentive to participate in sunrise/landrush auctions. */
- AUCTION("Auction Credit"),
-
- /** Credit awarded as part of a promotional deal. */
- PROMOTION("Promotional Credit");
-
- /** A descriptive name for a credit of this type. */
- private final String descriptiveName;
-
- CreditType(String descriptiveName) {
- this.descriptiveName = descriptiveName;
- }
-
- public String getDescriptiveName() {
- return descriptiveName;
- }
- }
-
- @Id
- long id;
-
- /** The registrar to whom this credit belongs. */
- @Parent
- Key parent;
-
- /** The type of credit. */
- CreditType type;
-
- /**
- * The time that this credit was created. If a registrar has multiple credits of a given type,
- * the older credits will be applied first.
- */
- DateTime creationTime;
-
- /** The currency in which the balance for this credit is stored. */
- CurrencyUnit currency;
-
- /** The line item description to use when displaying this credit on an invoice. */
- String description;
-
- /**
- * The TLD in which this credit applies.
- *
- *
For auction credits, this is also the TLD for which the relevant auctions occurred.
- */
- String tld;
-
- public Key getParent() {
- return parent;
- }
-
- public CreditType getType() {
- return type;
- }
-
- public DateTime getCreationTime() {
- return creationTime;
- }
-
- public CurrencyUnit getCurrency() {
- return currency;
- }
-
- public String getDescription() {
- return description;
- }
-
- public String getTld() {
- return tld;
- }
-
- /** Returns a string representation of this credit. */
- public String getSummary() {
- String fields = Joiner.on(' ').join(type, creationTime, tld);
- return String.format("%s (%s/%d) - %s", description, parent.getName(), id, fields);
- }
-
- /** Returns the default description for this {@link RegistrarCredit} instance. */
- private String getDefaultDescription() {
- return type.getDescriptiveName() + " for ." + tld;
- }
-
- @Override
- public Builder asBuilder() {
- return new Builder(clone(this));
- }
-
- /** A Builder for {@link RegistrarCredit}. */
- public static class Builder extends Buildable.Builder {
- public Builder() {}
-
- public Builder(RegistrarCredit instance) {
- super(instance);
- }
-
- public Builder setParent(Registrar parent) {
- getInstance().parent = Key.create(parent);
- return this;
- }
-
- public Builder setType(CreditType type) {
- getInstance().type = type;
- return this;
- }
-
- public Builder setCreationTime(DateTime creationTime) {
- getInstance().creationTime = creationTime;
- return this;
- }
-
- public Builder setCurrency(CurrencyUnit currency) {
- getInstance().currency = currency;
- return this;
- }
-
- public Builder setDescription(String description) {
- getInstance().description = description;
- return this;
- }
-
- public Builder setTld(String tld) {
- getInstance().tld = tld;
- return this;
- }
-
- @Override
- public RegistrarCredit build() {
- RegistrarCredit instance = getInstance();
- checkNotNull(instance.parent, "parent credit");
- checkNotNull(instance.type, "type");
- checkNotNull(instance.creationTime, "creationTime");
- checkNotNull(instance.currency, "currency");
- assertTldExists(checkNotNull(instance.tld, "tld"));
- checkArgument(
- Registry.get(instance.tld).getCurrency().equals(instance.currency),
- "Credits must be in the currency of the assigned TLD");
- instance.description =
- Optional.ofNullable(instance.description).orElse(instance.getDefaultDescription());
- return super.build();
- }
- }
-
- /** Ordering that sorts credits first by type and then by creation time. */
- private static final Ordering CREDIT_PRIORITY_ORDERING =
- new Ordering() {
- @Override
- public int compare(RegistrarCredit left, RegistrarCredit right) {
- return ComparisonChain.start()
- .compare(left.type, right.type)
- .compare(left.creationTime, right.creationTime)
- .result();
- }
- };
-
- /**
- * Loads all RegistrarCredit entities for the given Registrar.
- *
- *
The resulting list sorts the credits first by type and then by creation time.
- */
- public static ImmutableList loadAllForRegistrar(Registrar registrar) {
- return Streams.stream(ofy().load().type(RegistrarCredit.class).ancestor(registrar))
- .sorted(CREDIT_PRIORITY_ORDERING)
- .collect(toImmutableList());
- }
-}
diff --git a/java/google/registry/model/billing/RegistrarCreditBalance.java b/java/google/registry/model/billing/RegistrarCreditBalance.java
deleted file mode 100644
index 9090eb395..000000000
--- a/java/google/registry/model/billing/RegistrarCreditBalance.java
+++ /dev/null
@@ -1,251 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.model.billing;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static google.registry.model.ofy.ObjectifyService.ofy;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ForwardingNavigableMap;
-import com.google.common.collect.ImmutableSortedMap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Ordering;
-import com.googlecode.objectify.Key;
-import com.googlecode.objectify.annotation.Entity;
-import com.googlecode.objectify.annotation.Id;
-import com.googlecode.objectify.annotation.Parent;
-import google.registry.model.Buildable;
-import google.registry.model.ImmutableObject;
-import google.registry.model.annotations.ReportedOn;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import org.joda.money.CurrencyUnit;
-import org.joda.money.Money;
-import org.joda.time.DateTime;
-
-/**
- * The balance of a {@link RegistrarCredit} at a given point in time.
- *
- *
A credit balance has two related times in addition to the monetary amount: the effective time,
- * which represents the time at which the amount becomes the actual credit balance; and the
- * written time, which represents the time at which this balance object was saved.
- *
- *
The active balance of a credit object before (at) any given point in time T can be found by
- * taking the balance object with the latest effective time that is before (before or at) T, and
- * breaking any ties by choosing the mostly recently written among those balances.
- *
- *
NOTE: While credits are tracked within the model, there is no built-in support for actually
- * incorporating these credits into the generated billing data, and the model support for credits
- * should be considered quasi-deprecated (it may be removed without notice).
- */
-@ReportedOn
-@Entity
-public final class RegistrarCreditBalance extends ImmutableObject implements Buildable {
-
- @Id
- long id;
-
- /** The registrar credit object for which this represents a balance. */
- @Parent
- Key parent;
-
- /** The time at which this balance amount should become effective. */
- DateTime effectiveTime;
-
- /**
- * The time at which this balance update was written.
- *
- *
Used to break ties in cases where there are multiple balances with the same effective time,
- * as the last written balance will take priority.
- */
- DateTime writtenTime;
-
- /** The monetary amount of credit balance remaining as of the effective time. */
- Money amount;
-
- public Key getParent() {
- return parent;
- }
-
- public DateTime getEffectiveTime() {
- return effectiveTime;
- }
-
- public DateTime getWrittenTime() {
- return writtenTime;
- }
-
- public Money getAmount() {
- return amount;
- }
-
- @Override
- public Builder asBuilder() {
- return new Builder(clone(this));
- }
-
- /** A Builder for an {@link RegistrarCreditBalance}. */
- public static class Builder extends Buildable.Builder {
-
- private CurrencyUnit currency;
-
- public Builder() {}
-
- public Builder(RegistrarCreditBalance instance) {
- super(instance);
- }
-
- public RegistrarCreditBalance.Builder setParent(RegistrarCredit parent) {
- this.currency = parent.getCurrency();
- getInstance().parent = Key.create(parent);
- return this;
- }
-
- public RegistrarCreditBalance.Builder setEffectiveTime(DateTime effectiveTime) {
- getInstance().effectiveTime = effectiveTime;
- return this;
- }
-
- public RegistrarCreditBalance.Builder setWrittenTime(DateTime writtenTime) {
- getInstance().writtenTime = writtenTime;
- return this;
- }
-
- public RegistrarCreditBalance.Builder setAmount(Money amount) {
- checkArgument(amount.isPositiveOrZero(), "Credit balance amount cannot be negative");
- getInstance().amount = amount;
- return this;
- }
-
- @Override
- public RegistrarCreditBalance build() {
- RegistrarCreditBalance instance = getInstance();
- checkNotNull(instance.parent);
- checkNotNull(instance.effectiveTime);
- checkNotNull(instance.writtenTime);
- checkNotNull(instance.amount);
- checkState(
- instance.amount.getCurrencyUnit().equals(currency),
- "Currency of balance amount differs from credit currency (%s vs %s)",
- instance.amount.getCurrencyUnit(),
- currency);
- return super.build();
- }
- }
-
- /**
- * A map of maps representing the historical credit balance information for a given credit.
- *
- *
Specifically, this class provides a high-level view of the balances for a given credit
- * by in essence grouping them first by effective time and then by written time. This facilitates
- * the printing of a readable representation of a credit's balance history, and the retrieval of
- * the active balance at a given time (as described above on RegistrarCreditBalance).
- */
- public static class BalanceMap
- extends ForwardingNavigableMap> {
-
- /**
- * Constructs a BalanceMap for the given registrar credit by loading all RegistrarCreditBalance
- * entities for the credit and then inserting them into a map of maps keyed first by effective
- * time and then by written time with the balance amount as the value.
- */
- public static BalanceMap createForCredit(RegistrarCredit registrarCredit) {
- // Build up the data in a mutable map of maps.
- Map> map = new HashMap<>();
- for (RegistrarCreditBalance balance :
- ofy().load().type(RegistrarCreditBalance.class).ancestor(registrarCredit)) {
- // Create the submap at this key if it doesn't exist already.
- Map submap =
- Optional.ofNullable(map.get(balance.effectiveTime)).orElse(new HashMap<>());
- submap.put(balance.writtenTime, balance.amount);
- map.put(balance.effectiveTime, submap);
- }
- // Wrap the mutable map of maps in an immutable BalanceMap.
- return new BalanceMap(map);
- }
-
- /** The immutable map of maps used as the backing map. */
- private final ImmutableSortedMap> delegate;
-
- /**
- * Constructs an immutable BalanceMap from balance data provided as a map of maps.
- *
- *
The constructed BalanceMap delegates to an immutable copy of the provided map of maps.
- * This copy is created by first making a view of the map in which each submap is replaced by
- * an immutable copy, and then making an immutable copy of that view.
- */
- @VisibleForTesting
- BalanceMap(Map> data) {
- delegate =
- ImmutableSortedMap.copyOf(
- Maps.transformValues(
- data,
- (Map map) -> ImmutableSortedMap.copyOf(map, Ordering.natural())),
- Ordering.natural());
- }
-
- @Override
- protected ImmutableSortedMap> delegate() {
- return delegate;
- }
-
- /**
- * Returns the most recently written balance for the effective time corresponding to this entry,
- * or {@link Optional#absent()} if this entry is null.
- */
- private Optional getMostRecentlyWrittenBalance(
- Map.Entry> balancesAtEffectiveTime) {
- return balancesAtEffectiveTime == null
- ? Optional.empty()
- // Don't use Optional.ofNullable() here since it's an error if there's a empty submap.
- : Optional.of(balancesAtEffectiveTime.getValue().lastEntry().getValue());
- }
-
- /**
- * Returns the active balance at a given time as described above on RegistrarCreditBalance, or
- * {@link Optional#absent()} if no balance was active at that time (i.e. the time provided is
- * before the first effectiveTime of any balance for the credit this BalanceMap represents).
- */
- public Optional getActiveBalanceAtTime(DateTime time) {
- return getMostRecentlyWrittenBalance(delegate.floorEntry(time));
- }
-
- /**
- * Returns the active balance before a given time as described above on RegistrarCreditBalance,
- * or {@link Optional#absent()} if no balance was active before that time (i.e. the time
- * provided is before or at the first effectiveTime of any balance for the credit).
- */
- public Optional getActiveBalanceBeforeTime(DateTime time) {
- return getMostRecentlyWrittenBalance(delegate.lowerEntry(time));
- }
-
- /** Returns a string representation of this BalanceMap's data. */
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- for (Map.Entry> entry : delegate.entrySet()) {
- builder.append(String.format(" - %s\n", entry.getKey()));
- for (Map.Entry subEntry : entry.getValue().entrySet()) {
- builder.append(
- String.format(" - %s - %s\n", subEntry.getKey(), subEntry.getValue()));
- }
- }
- return builder.toString();
- }
- }
-}
diff --git a/java/google/registry/tools/CreateAuctionCreditsCommand.java b/java/google/registry/tools/CreateAuctionCreditsCommand.java
deleted file mode 100644
index 39308e28f..000000000
--- a/java/google/registry/tools/CreateAuctionCreditsCommand.java
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.tools;
-
-import static com.google.common.base.CaseFormat.UPPER_CAMEL;
-import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static google.registry.model.registry.Registries.assertTldExists;
-import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
-import static org.joda.time.DateTimeZone.UTC;
-
-import com.beust.jcommander.Parameter;
-import com.beust.jcommander.Parameters;
-import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Streams;
-import com.google.re2j.Matcher;
-import com.google.re2j.Pattern;
-import google.registry.model.billing.RegistrarCredit;
-import google.registry.model.billing.RegistrarCredit.CreditType;
-import google.registry.model.billing.RegistrarCreditBalance;
-import google.registry.model.registrar.Registrar;
-import google.registry.model.registry.Registry;
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.Stream;
-import org.joda.money.BigMoney;
-import org.joda.money.CurrencyUnit;
-import org.joda.money.Money;
-import org.joda.time.DateTime;
-
-/**
- * Command for creating new auction credits based on a CSV file from Pool.
- *
- *
The CSV file from the auction provider uses double-quotes around every field, so in order to
- * extract the raw field value we strip off the quotes after splitting each line by commas. We are
- * using a simple parsing strategy that does not support embedded quotation marks, commas, or
- * newlines.
- *
- *
We only care about three fields: 1) the "Affiliate" field which corresponds to the registrar
- * clientId stored in Datastore, and which we use to determine which registrar gets the credit, 2)
- * the "Commissions" field which contains the amount of the auction credit (as determined by logic
- * on the auction provider's side, see the Finance Requirements Doc for more information), and 3)
- * the "CurrencyCode" field, which we validate matches the TLD-wide currency for this TLD.
- */
-// TODO(b/16009815): Switch this file to using a real CSV parser.
-@Parameters(separators = " =", commandDescription = "Create new auction credits based on CSV")
-final class CreateAuctionCreditsCommand extends MutatingCommand {
-
- @Parameter(
- names = "--input_file",
- description = "CSV file for the Pool.com commissions report",
- required = true)
- private Path inputFile;
-
- @Parameter(
- names = {"-t", "--tld"},
- description = "The TLD corresponding to this commissions report",
- required = true)
- private String tld;
-
- @Parameter(
- names = "--effective_time",
- description = "The time at which these auction credits should become effective",
- required = true)
- private DateTime effectiveTime;
-
- /** Enum containing the headers we expect in the Pool.com CSV file, in order. */
- private enum CsvHeader {
- AFFILIATE,
- DOMAIN_NAME,
- EMAIL,
- BIDDER_ID,
- BIDDER_STATUS,
- UPDATED_AT,
- SALE_PRICE,
- COMMISSIONS,
- CURRENCY_CODE;
-
- public static List getHeaders() {
- return Stream.of(values())
- .map(header -> UPPER_UNDERSCORE.to(UPPER_CAMEL, header.name()))
- .collect(toImmutableList());
- }
- }
-
- private static final Pattern QUOTED_STRING = Pattern.compile("\"(.*)\"");
-
- /** Helper function to unwrap a quoted string, failing if the string is not quoted. */
- private static final Function UNQUOTER =
- input -> {
- Matcher matcher = QUOTED_STRING.matcher(input);
- checkArgument(matcher.matches(), "Input not quoted");
- return matcher.group(1);
- };
-
- /** Returns the input string of quoted CSV values split into the list of unquoted values. */
- private static List splitCsvLine(String line) {
- return Streams.stream(Splitter.on(',').split(line)).map(UNQUOTER).collect(toImmutableList());
- }
-
- @Override
- protected void init() throws Exception {
- assertTldExists(tld);
- ImmutableMultimap creditMap = parseCreditsFromCsv(inputFile, tld);
- stageCreditCreations(creditMap);
- }
-
- /**
- * Parses the provided CSV file of data from the auction provider and returns a multimap mapping
- * each registrar to the collection of auction credit amounts from this TLD's auctions that should
- * be awarded to this registrar, and validating that every credit amount's currency is in the
- * specified TLD-wide currency.
- */
- private static ImmutableMultimap parseCreditsFromCsv(
- Path csvFile, String tld) throws IOException {
- List lines = Files.readAllLines(csvFile, StandardCharsets.UTF_8);
- checkArgument(CsvHeader.getHeaders().equals(splitCsvLine(lines.get(0))),
- "Expected CSV header line not present");
- ImmutableMultimap.Builder builder = new ImmutableMultimap.Builder<>();
- for (String line : Iterables.skip(lines, 1)) {
- List fields = splitCsvLine(line);
- checkArgument(CsvHeader.getHeaders().size() == fields.size(), "Wrong number of fields");
- try {
- String clientId = fields.get(CsvHeader.AFFILIATE.ordinal());
- Registrar registrar =
- checkArgumentPresent(
- Registrar.loadByClientId(clientId), "Registrar %s not found", clientId);
- CurrencyUnit tldCurrency = Registry.get(tld).getCurrency();
- CurrencyUnit currency = CurrencyUnit.of((fields.get(CsvHeader.CURRENCY_CODE.ordinal())));
- checkArgument(
- tldCurrency.equals(currency),
- "Credit in wrong currency (%s should be %s)",
- currency,
- tldCurrency);
- // We use BigDecimal and BigMoney to preserve fractional currency units when computing the
- // total amount of each credit (since auction credits are percentages of winning bids).
- BigDecimal creditAmount = new BigDecimal(fields.get(CsvHeader.COMMISSIONS.ordinal()));
- BigMoney credit = BigMoney.of(currency, creditAmount);
- builder.put(registrar, credit);
- } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
- throw new IllegalArgumentException("Error in line: " + line, e);
- }
- }
- return builder.build();
- }
-
- /**
- * Stages the creation of RegistrarCredit and RegistrarCreditBalance instances for each
- * registrar in the provided multimap of credit amounts by registrar. The balance instance
- * created is the total of all the credit amounts for a given registrar.
- */
- private void stageCreditCreations(ImmutableMultimap creditMap) {
- DateTime now = DateTime.now(UTC);
- CurrencyUnit currency = Registry.get(tld).getCurrency();
- for (Registrar registrar : creditMap.keySet()) {
- // Use RoundingMode.UP to be nice and give registrars the extra fractional units.
- Money totalAmount =
- BigMoney.total(currency, creditMap.get(registrar)).toMoney(RoundingMode.UP);
- System.out.printf("Total auction credit balance for %s: %s\n",
- registrar.getClientId(), totalAmount);
-
- // Create the actual credit and initial credit balance.
- RegistrarCredit credit = new RegistrarCredit.Builder()
- .setParent(registrar)
- .setType(CreditType.AUCTION)
- .setCreationTime(now)
- .setCurrency(currency)
- .setTld(tld)
- .build();
- RegistrarCreditBalance creditBalance = new RegistrarCreditBalance.Builder()
- .setParent(credit)
- .setEffectiveTime(effectiveTime)
- .setWrittenTime(now)
- .setAmount(totalAmount)
- .build();
- stageEntityChange(null, credit);
- stageEntityChange(null, creditBalance);
- }
- }
-}
diff --git a/java/google/registry/tools/CreateCreditBalanceCommand.java b/java/google/registry/tools/CreateCreditBalanceCommand.java
deleted file mode 100644
index 1f426c2fd..000000000
--- a/java/google/registry/tools/CreateCreditBalanceCommand.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.tools;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static google.registry.model.ofy.ObjectifyService.ofy;
-import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
-
-import com.beust.jcommander.Parameter;
-import com.beust.jcommander.Parameters;
-import google.registry.model.billing.RegistrarCredit;
-import google.registry.model.billing.RegistrarCreditBalance;
-import google.registry.model.registrar.Registrar;
-import google.registry.util.SystemClock;
-import org.joda.money.Money;
-import org.joda.time.DateTime;
-
-/** Command for creating a new balance for a registrar credit. */
-@Parameters(separators = " =", commandDescription = "Create a new registrar credit balance")
-final class CreateCreditBalanceCommand extends MutatingCommand {
-
- @Parameter(
- names = "--registrar",
- description = "Client ID of the registrar owning the credit to create a new balance for",
- required = true)
- private String clientId;
-
- @Parameter(
- names = "--credit_id",
- description = "ID of credit to create a new balance for",
- required = true)
- private long creditId;
-
- @Parameter(
- names = "--balance",
- description = "The new balance amount",
- required = true)
- private Money balance;
-
- @Parameter(
- names = "--effective_time",
- description = "Point in time at which the new balance amount becomes effective",
- required = true)
- private DateTime effectiveTime;
-
- @Override
- public void init() {
- Registrar registrar =
- checkArgumentPresent(
- Registrar.loadByClientId(clientId), "Registrar %s not found", clientId);
- RegistrarCredit credit = ofy().load()
- .type(RegistrarCredit.class)
- .parent(registrar)
- .id(creditId)
- .now();
- checkNotNull(credit, "Registrar credit for %s with ID %s not found", clientId, creditId);
- RegistrarCreditBalance newBalance = new RegistrarCreditBalance.Builder()
- .setParent(credit)
- .setEffectiveTime(effectiveTime)
- .setWrittenTime(new SystemClock().nowUtc())
- .setAmount(balance)
- .build();
- stageEntityChange(null, newBalance);
- }
-}
diff --git a/java/google/registry/tools/CreateCreditCommand.java b/java/google/registry/tools/CreateCreditCommand.java
deleted file mode 100644
index 58f6c2452..000000000
--- a/java/google/registry/tools/CreateCreditCommand.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2017 The Nomulus Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package google.registry.tools;
-
-import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
-import static org.joda.time.DateTimeZone.UTC;
-
-import com.beust.jcommander.Parameter;
-import com.beust.jcommander.Parameters;
-import google.registry.model.billing.RegistrarCredit;
-import google.registry.model.billing.RegistrarCredit.CreditType;
-import google.registry.model.billing.RegistrarCreditBalance;
-import google.registry.model.registrar.Registrar;
-import javax.annotation.Nullable;
-import org.joda.money.Money;
-import org.joda.time.DateTime;
-
-/** Command for creating a registrar credit object with an initial balance. */
-@Parameters(separators = " =", commandDescription = "Create a new registrar credit")
-final class CreateCreditCommand extends MutatingCommand {
-
- @Parameter(
- names = "--registrar",
- description = "Client ID of the registrar who will be awarded this credit",
- required = true)
- private String clientId;
-
- @Parameter(
- names = "--type",
- description = "Type of credit (AUCTION or PROMOTION)",
- required = true)
- private CreditType type;
-
- @Nullable
- @Parameter(
- names = "--description",
- description = "Custom description that will appear on invoice line for this credit")
- private String description;
-
- @Parameter(
- names = "--tld",
- description = "TLD for which this credit applies",
- required = true)
- private String tld;
-
- @Parameter(
- names = "--balance",
- description = "Initial balance of this credit",
- required = true)
- private Money balance;
-
- @Parameter(
- names = "--effective_time",
- description = "Point in time at which the initial balance becomes effective",
- required = true)
- private DateTime effectiveTime;
-
- @Override
- protected void init() {
- DateTime now = DateTime.now(UTC);
- Registrar registrar =
- checkArgumentPresent(
- Registrar.loadByClientId(clientId), "Registrar %s not found", clientId);
- RegistrarCredit credit = new RegistrarCredit.Builder()
- .setParent(registrar)
- .setType(type)
- .setCreationTime(now)
- .setCurrency(balance.getCurrencyUnit())
- .setDescription(description)
- .setTld(tld)
- .build();
- RegistrarCreditBalance creditBalance = new RegistrarCreditBalance.Builder()
- .setParent(credit)
- .setEffectiveTime(effectiveTime)
- .setWrittenTime(now)
- .setAmount(balance)
- .build();
- stageEntityChange(null, credit);
- stageEntityChange(null, creditBalance);
- }
-}
diff --git a/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java b/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
index e1f126565..e1b88cab1 100644
--- a/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
+++ b/java/google/registry/tools/CreateOrUpdateRegistrarCommand.java
@@ -15,7 +15,6 @@
package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.isNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
@@ -29,7 +28,6 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
-import google.registry.model.billing.RegistrarBillingUtils;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.BillingMethod;
import google.registry.model.registrar.RegistrarAddress;
@@ -51,7 +49,6 @@ import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.joda.money.CurrencyUnit;
-import org.joda.money.Money;
import org.joda.time.DateTime;
/** Shared base class for commands to create or update a {@link Registrar}. */
@@ -370,18 +367,7 @@ abstract class CreateOrUpdateRegistrarCommand extends MutatingCommand {
newBillingAccountMap.putAll(billingAccountMap);
builder.setBillingAccountMap(newBillingAccountMap);
}
- if (billingMethod != null) {
- if (oldRegistrar != null && !billingMethod.equals(oldRegistrar.getBillingMethod())) {
- Map balances = RegistrarBillingUtils.loadBalance(oldRegistrar);
- for (Money balance : balances.values()) {
- checkState(balance.isZero(),
- "Refusing to change billing method on Registrar '%s' from %s to %s"
- + " because current balance is non-zero: %s",
- clientId, oldRegistrar.getBillingMethod(), billingMethod, balances);
- }
- }
- builder.setBillingMethod(billingMethod);
- }
+ Optional.ofNullable(billingMethod).ifPresent(builder::setBillingMethod);
List