diff --git a/QRCoder/PayloadGenerator/Girocode.cs b/QRCoder/PayloadGenerator/Girocode.cs
index fedd794d..100daf74 100644
--- a/QRCoder/PayloadGenerator/Girocode.cs
+++ b/QRCoder/PayloadGenerator/Girocode.cs
@@ -12,7 +12,7 @@ public class Girocode : Payload
private readonly string _br = "\n";
private readonly string _iban, _bic, _name, _purposeOfCreditTransfer, _remittanceInformation, _messageToGirocodeUser;
- private readonly decimal _amount;
+ private readonly decimal? _amount;
private readonly GirocodeVersion _version;
private readonly GirocodeEncoding _encoding;
private readonly TypeOfRemittance _typeOfRemittance;
@@ -28,7 +28,7 @@ public class Girocode : Payload
/// Account number of the Beneficiary. Only IBAN is allowed.
/// BIC of the Beneficiary Bank.
/// Name of the Beneficiary.
- /// Amount of the Credit Transfer in Euro. (Amount must be more than 0.01 and less than 999999999.99)
+ /// Amount of the Credit Transfer in Euro, from 0.01 to 999999999.99 inclusive, with at most two decimal places.
/// Remittance Information (Purpose-/reference text). (optional)
/// Type of remittance information. Either structured (e.g. ISO 11649 RF Creditor Reference) and max. 35 chars or unstructured and max. 140 chars.
/// Purpose of the Credit Transfer (optional)
@@ -37,6 +37,25 @@ public class Girocode : Payload
/// Encoding of the Girocode payload. Default: ISO-8859-1
/// Thrown when the input values are not valid according to the Girocode specification.
public Girocode(string iban, string? bic, string name, decimal amount, string remittanceInformation = "", TypeOfRemittance typeOfRemittance = TypeOfRemittance.Unstructured, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", GirocodeVersion version = GirocodeVersion.Version1, GirocodeEncoding encoding = GirocodeEncoding.ISO_8859_1)
+ : this(iban, bic, name, (decimal?)amount, remittanceInformation, typeOfRemittance, purposeOfCreditTransfer, messageToGirocodeUser, version, encoding)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class, which contains a payload for a Girocode (QR-Code with credit transfer information).
+ ///
+ /// Account number of the Beneficiary. Only IBAN is allowed.
+ /// BIC of the Beneficiary Bank.
+ /// Name of the Beneficiary.
+ /// Amount of the Credit Transfer in Euro, from 0.01 to 999999999.99 inclusive, with at most two decimal places. Null or omitted leaves the amount empty for the payer to enter.
+ /// Remittance Information (Purpose-/reference text). (optional)
+ /// Type of remittance information. Either structured (e.g. ISO 11649 RF Creditor Reference) and max. 35 chars or unstructured and max. 140 chars.
+ /// Purpose of the Credit Transfer (optional)
+ /// Beneficiary to originator information. (optional)
+ /// Girocode version. Either 001 or 002. Default: 001.
+ /// Encoding of the Girocode payload. Default: ISO-8859-1
+ /// Thrown when the input values are not valid according to the Girocode specification.
+ public Girocode(string iban, string? bic, string name, decimal? amount = null, string remittanceInformation = "", TypeOfRemittance typeOfRemittance = TypeOfRemittance.Unstructured, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", GirocodeVersion version = GirocodeVersion.Version1, GirocodeEncoding encoding = GirocodeEncoding.ISO_8859_1)
{
_version = version;
_encoding = encoding;
@@ -49,7 +68,7 @@ public Girocode(string iban, string? bic, string name, decimal amount, string re
if (name.Length > 70)
throw new GirocodeException("(Payee-)Name must be shorter than 71 chars.");
_name = name;
- if (amount.ToString(CultureInfo.InvariantCulture).Contains('.') && amount.ToString(CultureInfo.InvariantCulture).Split('.')[1].TrimEnd('0').Length > 2)
+ if (amount.HasValue && amount.Value.ToString(CultureInfo.InvariantCulture).Contains('.') && amount.Value.ToString(CultureInfo.InvariantCulture).Split('.')[1].TrimEnd('0').Length > 2)
throw new GirocodeException("Amount must have less than 3 digits after decimal point.");
if (amount < 0.01m || amount > 999999999.99m)
throw new GirocodeException("Amount has to be at least 0.01 and must be smaller or equal to 999999999.99.");
@@ -81,7 +100,7 @@ public override string ToString()
girocodePayload += _bic + _br;
girocodePayload += _name + _br;
girocodePayload += _iban + _br;
- girocodePayload += string.Format(CultureInfo.InvariantCulture, "EUR{0:0.00}", _amount) + _br;
+ girocodePayload += (_amount.HasValue ? string.Format(CultureInfo.InvariantCulture, "EUR{0:0.00}", _amount.Value) : string.Empty) + _br;
girocodePayload += _purposeOfCreditTransfer + _br;
girocodePayload += ((_typeOfRemittance == TypeOfRemittance.Structured)
? _remittanceInformation
@@ -91,6 +110,8 @@ public override string ToString()
: string.Empty) + _br;
girocodePayload += _messageToGirocodeUser;
+ girocodePayload = girocodePayload.TrimEnd('\n');
+
return ConvertStringToEncoding(girocodePayload, _encoding.ToString().Replace("_", "-"));
}
diff --git a/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt b/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt
index fa119ea8..47de8dd3 100644
--- a/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt
+++ b/QRCoderApiTests/net35+net40+net50+netstandard20+netstandard21/QRCoder.approved.txt
@@ -457,6 +457,7 @@ namespace QRCoder
public class Girocode : QRCoder.PayloadGenerator.Payload
{
public Girocode(string iban, string? bic, string name, decimal amount, string remittanceInformation = "", QRCoder.PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance = 1, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", QRCoder.PayloadGenerator.Girocode.GirocodeVersion version = 0, QRCoder.PayloadGenerator.Girocode.GirocodeEncoding encoding = 1) { }
+ public Girocode(string iban, string? bic, string name, decimal? amount = default, string remittanceInformation = "", QRCoder.PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance = 1, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", QRCoder.PayloadGenerator.Girocode.GirocodeVersion version = 0, QRCoder.PayloadGenerator.Girocode.GirocodeEncoding encoding = 1) { }
public override QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; }
public override string ToString() { }
public enum GirocodeEncoding
diff --git a/QRCoderApiTests/net60/QRCoder.approved.txt b/QRCoderApiTests/net60/QRCoder.approved.txt
index c0599c3a..cbf67f98 100644
--- a/QRCoderApiTests/net60/QRCoder.approved.txt
+++ b/QRCoderApiTests/net60/QRCoder.approved.txt
@@ -462,6 +462,7 @@ namespace QRCoder
public class Girocode : QRCoder.PayloadGenerator.Payload
{
public Girocode(string iban, string? bic, string name, decimal amount, string remittanceInformation = "", QRCoder.PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance = 1, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", QRCoder.PayloadGenerator.Girocode.GirocodeVersion version = 0, QRCoder.PayloadGenerator.Girocode.GirocodeEncoding encoding = 1) { }
+ public Girocode(string iban, string? bic, string name, decimal? amount = default, string remittanceInformation = "", QRCoder.PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance = 1, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", QRCoder.PayloadGenerator.Girocode.GirocodeVersion version = 0, QRCoder.PayloadGenerator.Girocode.GirocodeEncoding encoding = 1) { }
public override QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; }
public override string ToString() { }
public enum GirocodeEncoding
diff --git a/QRCoderApiTests/netstandard13/QRCoder.approved.txt b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
index f127e9ef..7602755a 100644
--- a/QRCoderApiTests/netstandard13/QRCoder.approved.txt
+++ b/QRCoderApiTests/netstandard13/QRCoder.approved.txt
@@ -400,6 +400,7 @@ namespace QRCoder
public class Girocode : QRCoder.PayloadGenerator.Payload
{
public Girocode(string iban, string? bic, string name, decimal amount, string remittanceInformation = "", QRCoder.PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance = 1, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", QRCoder.PayloadGenerator.Girocode.GirocodeVersion version = 0, QRCoder.PayloadGenerator.Girocode.GirocodeEncoding encoding = 1) { }
+ public Girocode(string iban, string? bic, string name, decimal? amount = default, string remittanceInformation = "", QRCoder.PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance = 1, string purposeOfCreditTransfer = "", string messageToGirocodeUser = "", QRCoder.PayloadGenerator.Girocode.GirocodeVersion version = 0, QRCoder.PayloadGenerator.Girocode.GirocodeEncoding encoding = 1) { }
public override QRCoder.QRCodeGenerator.ECCLevel EccLevel { get; }
public override string ToString() { }
public enum GirocodeEncoding
diff --git a/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs b/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs
index 95d40c6d..81a88bd6 100644
--- a/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs
+++ b/QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs
@@ -16,7 +16,7 @@ public void girocode_generator_can_generate_payload_minimal()
generator
.ToString()
- .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00\n\n\n\n");
+ .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nWikimedia Fördergesellschaft\nDE33100205000001194700\nEUR10.00");
}
[Fact]
@@ -341,7 +341,7 @@ public void girocode_generator_version2_with_null_bic_should_succeed()
payload
.ToString()
- .ShouldBe("BCD\n002\n1\nSCT\n\na name\nNL86INGB0002445588\nEUR1337.99\n\n\nsome info\n");
+ .ShouldBe("BCD\n002\n1\nSCT\n\na name\nNL86INGB0002445588\nEUR1337.99\n\n\nsome info");
}
[Fact]
@@ -363,7 +363,7 @@ public void girocode_generator_version2_with_empty_bic_should_succeed()
payload
.ToString()
- .ShouldBe("BCD\n002\n1\nSCT\n\na name\nNL86INGB0002445588\nEUR1337.99\n\n\nsome info\n");
+ .ShouldBe("BCD\n002\n1\nSCT\n\na name\nNL86INGB0002445588\nEUR1337.99\n\n\nsome info");
}
[Fact]
@@ -386,7 +386,7 @@ public void girocode_generator_version2_with_valid_bic_should_succeed()
payload
.ToString()
- .ShouldBe("BCD\n002\n1\nSCT\nINGBNL2A\na name\nNL86INGB0002445588\nEUR1337.99\n\n\nsome info\n");
+ .ShouldBe("BCD\n002\n1\nSCT\nINGBNL2A\na name\nNL86INGB0002445588\nEUR1337.99\n\n\nsome info");
}
[Fact]
@@ -455,4 +455,144 @@ public void girocode_generator_version1_with_empty_bic_should_throw_exception()
Assert.IsType(exception);
exception.Message.ShouldBe("The BIC entered isn't valid.");
}
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void girocode_generator_can_generate_payload_without_amount(bool omitAmount)
+ {
+ var iban = "DE33100205000001194700";
+ var bic = "BFSWDE33BER";
+ var name = "Payee";
+
+ var generator = omitAmount
+ ? new PayloadGenerator.Girocode(iban, bic, name)
+ : new PayloadGenerator.Girocode(iban, bic, name, amount: null);
+
+ generator
+ .ToString()
+ .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nPayee\nDE33100205000001194700");
+ }
+
+ [Theory]
+ [InlineData(false, "CHAR", "", PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, "", "\n\nCHAR")]
+ [InlineData(false, "", "Reference", PayloadGenerator.Girocode.TypeOfRemittance.Structured, "", "\n\n\nReference")]
+ [InlineData(false, "", "Reference", PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, "", "\n\n\n\nReference")]
+ [InlineData(false, "", "", PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, "Thank you ", "\n\n\n\n\nThank you ")]
+ [InlineData(true, "CHAR", "", PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, "", "\nEUR10.00\nCHAR")]
+ [InlineData(true, "", "Reference", PayloadGenerator.Girocode.TypeOfRemittance.Structured, "", "\nEUR10.00\n\nReference")]
+ [InlineData(true, "", "Reference", PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, "", "\nEUR10.00\n\n\nReference")]
+ [InlineData(true, "", "", PayloadGenerator.Girocode.TypeOfRemittance.Unstructured, "Thank you ", "\nEUR10.00\n\n\n\nThank you ")]
+ public void girocode_generator_should_end_payload_at_last_populated_field(
+ bool hasAmount,
+ string purposeOfCreditTransfer,
+ string remittanceInformation,
+ PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance,
+ string messageToGirocodeUser,
+ string expectedSuffix)
+ {
+ var iban = "DE33100205000001194700";
+ var bic = "BFSWDE33BER";
+ var name = "Payee";
+
+ var generator = new PayloadGenerator.Girocode(iban, bic, name, amount: hasAmount ? 10m : (decimal?)null,
+ remittanceInformation: remittanceInformation, typeOfRemittance: typeOfRemittance,
+ purposeOfCreditTransfer: purposeOfCreditTransfer, messageToGirocodeUser: messageToGirocodeUser);
+
+ generator
+ .ToString()
+ .ShouldBe("BCD\n001\n2\nSCT\nBFSWDE33BER\nPayee\nDE33100205000001194700" + expectedSuffix);
+ }
+
+ [Theory]
+ [InlineData(false, PayloadGenerator.Girocode.GirocodeVersion.Version1, PayloadGenerator.Girocode.TypeOfRemittance.Structured)]
+ [InlineData(false, PayloadGenerator.Girocode.GirocodeVersion.Version1, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured)]
+ [InlineData(false, PayloadGenerator.Girocode.GirocodeVersion.Version2, PayloadGenerator.Girocode.TypeOfRemittance.Structured)]
+ [InlineData(false, PayloadGenerator.Girocode.GirocodeVersion.Version2, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured)]
+ [InlineData(true, PayloadGenerator.Girocode.GirocodeVersion.Version1, PayloadGenerator.Girocode.TypeOfRemittance.Structured)]
+ [InlineData(true, PayloadGenerator.Girocode.GirocodeVersion.Version1, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured)]
+ [InlineData(true, PayloadGenerator.Girocode.GirocodeVersion.Version2, PayloadGenerator.Girocode.TypeOfRemittance.Structured)]
+ [InlineData(true, PayloadGenerator.Girocode.GirocodeVersion.Version2, PayloadGenerator.Girocode.TypeOfRemittance.Unstructured)]
+ public void girocode_generator_should_preserve_field_positions_without_amount(
+ bool omitAmount,
+ PayloadGenerator.Girocode.GirocodeVersion version,
+ PayloadGenerator.Girocode.TypeOfRemittance typeOfRemittance)
+ {
+ var iban = "DE33100205000001194700";
+ var bic = version == PayloadGenerator.Girocode.GirocodeVersion.Version1 ? "BFSWDE33BER" : null;
+ var name = "Payee";
+ var remittanceInformation = "Reference";
+ var purposeOfCreditTransfer = "CHAR";
+ var messageToGirocodeUser = "Thank you";
+
+ var generator = omitAmount
+ ? new PayloadGenerator.Girocode(iban, bic, name, remittanceInformation: remittanceInformation,
+ typeOfRemittance: typeOfRemittance, purposeOfCreditTransfer: purposeOfCreditTransfer,
+ messageToGirocodeUser: messageToGirocodeUser, version: version)
+ : new PayloadGenerator.Girocode(iban, bic, name, amount: null, remittanceInformation: remittanceInformation,
+ typeOfRemittance: typeOfRemittance, purposeOfCreditTransfer: purposeOfCreditTransfer,
+ messageToGirocodeUser: messageToGirocodeUser, version: version);
+
+ generator
+ .ToString()
+ .Split('\n')
+ .ShouldBe(new[]
+ {
+ "BCD",
+ version == PayloadGenerator.Girocode.GirocodeVersion.Version1 ? "001" : "002",
+ "2",
+ "SCT",
+ bic ?? string.Empty,
+ "Payee",
+ iban,
+ string.Empty,
+ "CHAR",
+ typeOfRemittance == PayloadGenerator.Girocode.TypeOfRemittance.Structured ? "Reference" : string.Empty,
+ typeOfRemittance == PayloadGenerator.Girocode.TypeOfRemittance.Unstructured ? "Reference" : string.Empty,
+ "Thank you",
+ });
+ }
+
+ [Theory]
+ [InlineData("0.01", "EUR0.01")]
+ [InlineData("999999999.99", "EUR999999999.99")]
+ [InlineData("12", "EUR12.00")]
+ [InlineData("12.3400", "EUR12.34")]
+ public void girocode_generator_should_format_specified_amount(string amountText, string expectedAmount)
+ {
+ var iban = "DE33100205000001194700";
+ var bic = "BFSWDE33BER";
+ var name = "Payee";
+ var amount = decimal.Parse(amountText, CultureInfo.InvariantCulture);
+
+ var generator = new PayloadGenerator.Girocode(iban, bic, name, amount);
+ var nullableGenerator = new PayloadGenerator.Girocode(iban, bic, name, (decimal?)amount);
+
+ generator
+ .ToString()
+ .ShouldBe($"BCD\n001\n2\nSCT\nBFSWDE33BER\nPayee\nDE33100205000001194700\n{expectedAmount}");
+ nullableGenerator
+ .ToString()
+ .ShouldBe(generator.ToString());
+ }
+
+ [Theory]
+ [InlineData("0", "Amount has to be at least 0.01 and must be smaller or equal to 999999999.99.")]
+ [InlineData("-0.01", "Amount has to be at least 0.01 and must be smaller or equal to 999999999.99.")]
+ [InlineData("1000000000", "Amount has to be at least 0.01 and must be smaller or equal to 999999999.99.")]
+ [InlineData("0.001", "Amount must have less than 3 digits after decimal point.")]
+ [InlineData("10.521", "Amount must have less than 3 digits after decimal point.")]
+ public void girocode_generator_should_throw_invalid_amount_exception(string amountText, string expectedMessage)
+ {
+ var iban = "DE33100205000001194700";
+ var bic = "BFSWDE33BER";
+ var name = "Payee";
+ var amount = decimal.Parse(amountText, CultureInfo.InvariantCulture);
+
+ var exception = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, amount));
+ var nullableException = Should.Throw(() => new PayloadGenerator.Girocode(iban, bic, name, (decimal?)amount));
+
+ exception.Message.ShouldBe(expectedMessage);
+ nullableException.Message.ShouldBe(expectedMessage);
+ }
}