diff --git a/Core/QRBill.cs b/Core/QRBill.cs
index 5e2b035..727cb49 100644
--- a/Core/QRBill.cs
+++ b/Core/QRBill.cs
@@ -312,7 +312,26 @@ public static string EncodeQrCodeText(Bill bill)
/// If he text could not be decoded or the decoded bill data is invalid.
public static Bill DecodeQrCodeText(string text)
{
- return QRCodeText.Decode(text);
+ return QRCodeText.Decode(text, false);
+ }
+
+ ///
+ /// Decodes the text from a QR code and fills it into a data structure
+ ///
+ /// A subset of the validations related to embedded QR code text is run. It the
+ /// validation fails, a is thrown containing
+ /// the validation result. See the error messages marked with a dagger in
+ /// Bill data
+ /// validation.
+ ///
+ ///
+ /// The text to decode.
+ /// If true is passed for this parameter, invalid values for the amount are also accepted (currently only "NULL" / "null"). In this case, the amount is set to null.
+ /// The decoded bill data.
+ /// If he text could not be decoded or the decoded bill data is invalid.
+ public static Bill DecodeQrCodeText(string text, bool allowInvalidAmount)
+ {
+ return QRCodeText.Decode(text, allowInvalidAmount);
}
private static ICanvas CreateCanvas(BillFormat format)
diff --git a/Core/QRCodeText.cs b/Core/QRCodeText.cs
index 9bd1a86..86a5bf0 100644
--- a/Core/QRCodeText.cs
+++ b/Core/QRCodeText.cs
@@ -127,7 +127,7 @@ private static string FormatAmountForCode(decimal amount)
}
private static readonly Regex ValidVersion = new Regex(@"^02\d\d$", RegexOptions.Compiled);
-
+
///
/// Decodes the specified text and returns the bill data.
///
@@ -139,10 +139,11 @@ private static string FormatAmountForCode(decimal amount)
///
///
/// The text to decode.
+ /// If true is passed for this parameter, invalid values for the amount are also accepted (currently only "NULL" / "null"). In this case, the amount is set to null.
/// The decoded bill data.
/// The text is in an invalid format.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Major Code Smell", "S1066:Mergeable \"if\" statements should be combined", Justification = "Easier to read the way it is")]
- public static Bill Decode(string text)
+ public static Bill Decode(string text, bool allowInvalidAmount)
{
var lines = SplitLines(text);
if (lines.Count < 31 || lines.Count > 34)
@@ -186,6 +187,10 @@ public static Bill Decode(string text)
{
billData.Amount = amount;
}
+ else if (allowInvalidAmount && lines[18].Equals("NULL", StringComparison.OrdinalIgnoreCase))
+ {
+ billData.Amount = null;
+ }
else
{
ThrowSingleValidationError(ValidationConstants.FieldAmount, ValidationConstants.KeyNumberInvalid);
diff --git a/CoreTest/CoreTest.csproj b/CoreTest/CoreTest.csproj
index a3ac532..6d180b7 100644
--- a/CoreTest/CoreTest.csproj
+++ b/CoreTest/CoreTest.csproj
@@ -28,7 +28,6 @@
-
-
+
diff --git a/CoreTest/DecodedTextTest.cs b/CoreTest/DecodedTextTest.cs
index 5c5e6e9..d6c4c54 100644
--- a/CoreTest/DecodedTextTest.cs
+++ b/CoreTest/DecodedTextTest.cs
@@ -176,6 +176,68 @@ public void DecodeMissingEpd()
() => QRBill.DecodeQrCodeText(invalidText));
TestHelper.AssertSingleError(err.Result, ValidationConstants.KeyDataStructureInvalid, ValidationConstants.FieldTrailer);
}
+
+ [Fact]
+ public void DecodeMethodShouldReturnValidBillWhenQrCodeTextIsValid()
+ {
+ var bill = QRBill.DecodeQrCodeText(SampleData.CreateQrCode1());
+ Assert.NotNull(bill);
+ Assert.Equal(Bill.QrBillStandardVersion.V2_0, bill.Version);
+ Assert.Equal(2500.00m, bill.Amount);
+ Assert.Equal("CHF", bill.Currency);
+ Assert.Equal("CH1234567890123456789", bill.Account);
+ Assert.Equal("Steuerverwaltung der Stadt Bern", bill.Creditor.Name);
+ Assert.Equal("Bundesgasse", bill.Creditor.Street);
+ Assert.Equal("33", bill.Creditor.HouseNo);
+ Assert.Equal("3011", bill.Creditor.PostalCode);
+ Assert.Equal("Bern", bill.Creditor.Town);
+ Assert.Equal("CH", bill.Creditor.CountryCode);
+ Assert.Equal("Martina Muster", bill.Debtor.Name);
+ Assert.Equal("Bubenbergplatz", bill.Debtor.Street);
+ Assert.Equal("1", bill.Debtor.HouseNo);
+ Assert.Equal("3011", bill.Debtor.PostalCode);
+ Assert.Equal("Bern", bill.Debtor.Town);
+ Assert.Equal("CH", bill.Debtor.CountryCode);
+ Assert.Equal("QRR", bill.ReferenceType);
+ Assert.Equal("123456789012345678901234567", bill.Reference);
+ Assert.Equal("1. Steuerrate 2020", bill.UnstructuredMessage);
+ Assert.Equal("//S1/11/200627/30/115140892/31/200627/32/7.7/40/0:30", bill.BillInformation);
+ }
+
+ [Fact]
+ public void DecodeMethodShouldHandleInvalidAmountIfParameterIsSupplied()
+ {
+ var bill = QRBill.DecodeQrCodeText(SampleData.CreateInvalidQrCode1(), true);
+ Assert.NotNull(bill);
+ Assert.Equal(Bill.QrBillStandardVersion.V2_0, bill.Version);
+ Assert.Null(bill.Amount);
+ Assert.Equal("CHF", bill.Currency);
+ Assert.Equal("CH8430000001800003797", bill.Account);
+ Assert.Equal("AXA Versicherungen AG", bill.Creditor.Name);
+ Assert.Equal("General-Guisan-Str.", bill.Creditor.Street);
+ Assert.Equal("40", bill.Creditor.HouseNo);
+ Assert.Equal("8401", bill.Creditor.PostalCode);
+ Assert.Equal("Winterthur", bill.Creditor.Town);
+ Assert.Equal("CH", bill.Creditor.CountryCode);
+ Assert.Equal("Testfirma AG", bill.Debtor.Name);
+ Assert.Equal("Hauptstrasse", bill.Debtor.Street);
+ Assert.Equal("61", bill.Debtor.HouseNo);
+ Assert.Equal("6210", bill.Debtor.PostalCode);
+ Assert.Equal("Sursee", bill.Debtor.Town);
+ Assert.Equal("CH", bill.Debtor.CountryCode);
+ Assert.Equal("QRR", bill.ReferenceType);
+ Assert.Equal("000000001000285497220812814", bill.Reference);
+ Assert.Equal("", bill.UnstructuredMessage);
+ Assert.Equal("", bill.BillInformation);
+ }
+
+ [Fact]
+ public void DecodeMethodShouldFailIfAmountIsInvalidValue()
+ {
+ QRBillValidationException err = Assert.Throws(
+ () => QRBill.DecodeQrCodeText(SampleData.CreateInvalidQrCode1()));
+ TestHelper.AssertSingleError(err.Result, ValidationConstants.KeyNumberInvalid, ValidationConstants.FieldAmount);
+ }
private class NewLineTheoryData : TheoryData
{
diff --git a/CoreTest/SampleData.cs b/CoreTest/SampleData.cs
index aa19f5d..9ed3a71 100644
--- a/CoreTest/SampleData.cs
+++ b/CoreTest/SampleData.cs
@@ -9,6 +9,7 @@
using System.Collections.Generic;
using System.Numerics;
using System.Security.Principal;
+using System.Text;
namespace Codecrete.SwissQRBill.CoreTest
@@ -265,5 +266,80 @@ public static Bill CreateExample8()
return bill;
}
+
+ public static string CreateQrCode1()
+ {
+ var qrCodeText = new StringBuilder();
+ qrCodeText.AppendLine("SPC");
+ qrCodeText.AppendLine("0200");
+ qrCodeText.AppendLine("1");
+ qrCodeText.AppendLine("CH1234567890123456789");
+ qrCodeText.AppendLine("S");
+ qrCodeText.AppendLine("Steuerverwaltung der Stadt Bern");
+ qrCodeText.AppendLine("Bundesgasse");
+ qrCodeText.AppendLine("33");
+ qrCodeText.AppendLine("3011");
+ qrCodeText.AppendLine("Bern");
+ qrCodeText.AppendLine("CH");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("2500.00");
+ qrCodeText.AppendLine("CHF");
+ qrCodeText.AppendLine("S");
+ qrCodeText.AppendLine("Martina Muster");
+ qrCodeText.AppendLine("Bubenbergplatz");
+ qrCodeText.AppendLine("1");
+ qrCodeText.AppendLine("3011");
+ qrCodeText.AppendLine("Bern");
+ qrCodeText.AppendLine("CH");
+ qrCodeText.AppendLine("QRR");
+ qrCodeText.AppendLine("123456789012345678901234567");
+ qrCodeText.AppendLine("1. Steuerrate 2020");
+ qrCodeText.AppendLine("EPD");
+ qrCodeText.AppendLine("//S1/11/200627/30/115140892/31/200627/32/7.7/40/0:30");
+ return qrCodeText.ToString();
+ }
+
+ public static string CreateInvalidQrCode1()
+ {
+ var qrCodeText = new StringBuilder();
+ qrCodeText.AppendLine("SPC");
+ qrCodeText.AppendLine("0200");
+ qrCodeText.AppendLine("1");
+ qrCodeText.AppendLine("CH8430000001800003797");
+ qrCodeText.AppendLine("S");
+ qrCodeText.AppendLine("AXA Versicherungen AG");
+ qrCodeText.AppendLine("General-Guisan-Str.");
+ qrCodeText.AppendLine("40");
+ qrCodeText.AppendLine("8401");
+ qrCodeText.AppendLine("Winterthur");
+ qrCodeText.AppendLine("CH");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("null");
+ qrCodeText.AppendLine("CHF");
+ qrCodeText.AppendLine("S");
+ qrCodeText.AppendLine("Testfirma AG");
+ qrCodeText.AppendLine("Hauptstrasse");
+ qrCodeText.AppendLine("61");
+ qrCodeText.AppendLine("6210");
+ qrCodeText.AppendLine("Sursee");
+ qrCodeText.AppendLine("CH");
+ qrCodeText.AppendLine("QRR");
+ qrCodeText.AppendLine("000000001000285497220812814");
+ qrCodeText.AppendLine("");
+ qrCodeText.AppendLine("EPD");
+ return qrCodeText.ToString();
+ }
}
}