Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions QRCoder/PayloadGenerator/Girocode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,7 +28,7 @@ public class Girocode : Payload
/// <param name="iban">Account number of the Beneficiary. Only IBAN is allowed.</param>
/// <param name="bic">BIC of the Beneficiary Bank.</param>
/// <param name="name">Name of the Beneficiary.</param>
/// <param name="amount">Amount of the Credit Transfer in Euro. (Amount must be more than 0.01 and less than 999999999.99)</param>
/// <param name="amount">Amount of the Credit Transfer in Euro, from 0.01 to 999999999.99 inclusive, with at most two decimal places.</param>
/// <param name="remittanceInformation">Remittance Information (Purpose-/reference text). (optional)</param>
/// <param name="typeOfRemittance">Type of remittance information. Either structured (e.g. ISO 11649 RF Creditor Reference) and max. 35 chars or unstructured and max. 140 chars.</param>
/// <param name="purposeOfCreditTransfer">Purpose of the Credit Transfer (optional)</param>
Expand All @@ -37,6 +37,25 @@ public class Girocode : Payload
/// <param name="encoding">Encoding of the Girocode payload. Default: ISO-8859-1</param>
/// <exception cref="GirocodeException">Thrown when the input values are not valid according to the Girocode specification.</exception>
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)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="Girocode"/> class, which contains a payload for a Girocode (QR-Code with credit transfer information).
/// </summary>
/// <param name="iban">Account number of the Beneficiary. Only IBAN is allowed.</param>
/// <param name="bic">BIC of the Beneficiary Bank.</param>
/// <param name="name">Name of the Beneficiary.</param>
/// <param name="amount">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.</param>
/// <param name="remittanceInformation">Remittance Information (Purpose-/reference text). (optional)</param>
/// <param name="typeOfRemittance">Type of remittance information. Either structured (e.g. ISO 11649 RF Creditor Reference) and max. 35 chars or unstructured and max. 140 chars.</param>
/// <param name="purposeOfCreditTransfer">Purpose of the Credit Transfer (optional)</param>
/// <param name="messageToGirocodeUser">Beneficiary to originator information. (optional)</param>
/// <param name="version">Girocode version. Either 001 or 002. Default: 001.</param>
/// <param name="encoding">Encoding of the Girocode payload. Default: ISO-8859-1</param>
/// <exception cref="GirocodeException">Thrown when the input values are not valid according to the Girocode specification.</exception>
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;
Expand All @@ -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.");
Expand Down Expand Up @@ -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;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
girocodePayload += _purposeOfCreditTransfer + _br;
girocodePayload += ((_typeOfRemittance == TypeOfRemittance.Structured)
? _remittanceInformation
Expand All @@ -91,6 +110,8 @@ public override string ToString()
: string.Empty) + _br;
girocodePayload += _messageToGirocodeUser;

girocodePayload = girocodePayload.TrimEnd('\n');

return ConvertStringToEncoding(girocodePayload, _encoding.ToString().Replace("_", "-"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions QRCoderApiTests/net60/QRCoder.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions QRCoderApiTests/netstandard13/QRCoder.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
148 changes: 144 additions & 4 deletions QRCoderTests/PayloadGeneratorTests/GirocodeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand Down Expand Up @@ -455,4 +455,144 @@ public void girocode_generator_version1_with_empty_bic_should_throw_exception()
Assert.IsType<PayloadGenerator.Girocode.GirocodeException>(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<PayloadGenerator.Girocode.GirocodeException>(() => new PayloadGenerator.Girocode(iban, bic, name, amount));
var nullableException = Should.Throw<PayloadGenerator.Girocode.GirocodeException>(() => new PayloadGenerator.Girocode(iban, bic, name, (decimal?)amount));

exception.Message.ShouldBe(expectedMessage);
nullableException.Message.ShouldBe(expectedMessage);
}
}