diff --git a/Source/Singulink.Globalization.Currency/SortedMoneySet.cs b/Source/Singulink.Globalization.Currency/SortedMoneySet.cs
index 9a0bdc5..fd04cb9 100644
--- a/Source/Singulink.Globalization.Currency/SortedMoneySet.cs
+++ b/Source/Singulink.Globalization.Currency/SortedMoneySet.cs
@@ -299,6 +299,28 @@ public void SetAmount(decimal amount, Currency currency)
_amountLookup[currency] = amount;
}
+ ///
+ /// Subtracts the specified value from this set. Zero amounts are not trimmed from the set.
+ ///
+ ///
+ /// Default values that are not associated with any currency are ignored.
+ ///
+ public void Subtract(Money value) => Add(-value);
+
+ ///
+ /// Subtracts the specified currency and amount from this set.
+ ///
+ public void Subtract(decimal amount, string currencyCode) => Add(-amount, currencyCode);
+
+ ///
+ /// Adds the specified currency and amount to this set.
+ ///
+ public void Subtract(decimal amount, Currency currency)
+ {
+ EnsureCurrencyAllowed(currency, nameof(currency));
+ AddInternal(amount, currency);
+ }
+
///
/// Copies the values in this set to a new immutable set that uses the same registry as this set.
///
diff --git a/Tests/Singulink.Globalization.Currency.Tests/SortedMoneySetTests/SubtractTests.cs b/Tests/Singulink.Globalization.Currency.Tests/SortedMoneySetTests/SubtractTests.cs
new file mode 100644
index 0000000..a1e406f
--- /dev/null
+++ b/Tests/Singulink.Globalization.Currency.Tests/SortedMoneySetTests/SubtractTests.cs
@@ -0,0 +1,124 @@
+using Shouldly;
+
+namespace Singulink.Globalization.Tests.SortedMoneySetTests;
+
+[TestClass]
+public class SubtractTests
+{
+ private static readonly Money Usd100 = new(100m, "USD");
+ private static readonly Money Cad50 = new(50m, "CAD");
+ private static readonly Money Eur25 = new(25m, "EUR");
+ private static readonly Money Aud75 = new(75m, "AUD");
+ private static readonly ImmutableSortedMoneySet ImmutableSet = [Usd100, Cad50, Eur25];
+
+ private readonly SortedMoneySet _set = ImmutableSet.ToSet();
+
+ // public void Subtract(Money value) tests
+
+ [TestMethod]
+ public void Money_CurrencyExists_SubtractsPositiveAmountAndReturnsPositiveAmount()
+ {
+ _set.Subtract(Usd100);
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([new(0m, "USD"), Cad50, Eur25]);
+ }
+
+ [TestMethod]
+ public void MoneyCurrencyExists_SubtractsZeroAmountAndReturnsPositiveAmount()
+ {
+ _set.Subtract(new(-0m, "EUR"));
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([Usd100, Cad50, Eur25]);
+ }
+
+ [TestMethod]
+ public void MoneyCurrencyNotInSet_AddsValueToSet()
+ {
+ _set.Subtract(-Aud75);
+ _set.ShouldBe([Usd100, Cad50, Eur25, Aud75]);
+ }
+
+ [TestMethod]
+ public void MoneyCurrencyDisallowed_ThrowsArgumentException()
+ {
+ var value = new Money(100, new Currency("Blah blah blah", "BBB", "$$", 2));
+
+ Should.Throw(() => _set.Subtract(value))
+ .Message.ShouldBe($"The currency '{value.Currency}' is not present in the set's currency registry. (Parameter 'value')");
+
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([Usd100, Cad50, Eur25]);
+ }
+
+ // public void Subtract(decimal amount, string currencyCode) tests
+
+ [TestMethod]
+ public void CurrencyCodeExists_SubtractsPositiveAmountAndReturnsPositiveAmount()
+ {
+ _set.Subtract(100m, "USD");
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([new(0m, "USD"), Cad50, Eur25]);
+ }
+
+ [TestMethod]
+ public void CurrencyCodeExists_SubtractsZeroAmountAndReturnsPositiveAmount()
+ {
+ _set.Subtract(-0m, "EUR");
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([Usd100, Cad50, Eur25]);
+ }
+
+ [TestMethod]
+ public void CurrencyCodeNotInSet_AddsValueToSet()
+ {
+ _set.Subtract(-75m, "AUD");
+ _set.ShouldBe([Usd100, Cad50, Eur25, Aud75]);
+ }
+
+ [TestMethod]
+ public void CurrencyCodeDisallowed_ThrowsArgumentException()
+ {
+ Should.Throw(() => _set.Subtract(100m, "Blah blah blah"))
+ .Message.ShouldBe("Currency code 'Blah blah blah' not found in the 'System' registry. (Parameter 'currencyCode')");
+
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([Usd100, Cad50, Eur25]);
+ }
+
+ // public void Subtract(decimal amount, Currency currency) tests
+
+ [TestMethod]
+ public void CurrencyExists_SubtractsPositiveAmountAndReturnsPositiveAmount()
+ {
+ _set.Subtract(-100m, Currency.Get("USD"));
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([new(0m, "USD"), Cad50, Eur25]);
+ }
+
+ [TestMethod]
+ public void CurrencyExists_SubtractsZeroAmountAndReturnsPositiveAmount()
+ {
+ _set.Subtract(-0m, Currency.Get("EUR"));
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([Usd100, Cad50, Eur25]);
+ }
+
+ [TestMethod]
+ public void CurrencyNotInSet_AddsValueToSet()
+ {
+ _set.Subtract(75m, Currency.Get("AUD"));
+ _set.ShouldBe([Usd100, Cad50, Eur25, Aud75]);
+ }
+
+ [TestMethod]
+ public void CurrencyDisallowed_ThrowsArgumentException()
+ {
+ var disallowedCurrency = new Currency("XXX", "Non-existent currency", "X", 2);
+
+ Should.Throw(() => _set.Subtract(100m, disallowedCurrency))
+ .Message.ShouldBe($"The currency '{disallowedCurrency}' is not present in the set's currency registry. (Parameter 'currency')");
+
+ _set.Count.ShouldBe(3);
+ _set.ShouldBe([Usd100, Cad50, Eur25]);
+ }
+}
\ No newline at end of file