Skip to content
Merged
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
21 changes: 20 additions & 1 deletion Core/QRBill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,26 @@ public static string EncodeQrCodeText(Bill bill)
/// <exception cref="QRBillValidationException">If he text could not be decoded or the decoded bill data is invalid.</exception>
public static Bill DecodeQrCodeText(string text)
{
return QRCodeText.Decode(text);
return QRCodeText.Decode(text, false);
}

/// <summary>
/// Decodes the text from a QR code and fills it into a <see cref="Bill"/> data structure
/// <para>
/// A subset of the validations related to embedded QR code text is run. It the
/// validation fails, a <see cref="QRBillValidationException"/> is thrown containing
/// the validation result. See the error messages marked with a dagger in
/// <a href="https://github.com/manuelbl/SwissQRBill/wiki/Bill-data-validation">Bill data
/// validation</a>.
/// </para>
/// </summary>
/// <param name="text">The text to decode.</param>
/// <param name="allowInvalidAmount">If <c>true</c> 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 <c>null</c>.</param>
/// <returns>The decoded bill data.</returns>
/// <exception cref="QRBillValidationException">If he text could not be decoded or the decoded bill data is invalid.</exception>
public static Bill DecodeQrCodeText(string text, bool allowInvalidAmount)
{
return QRCodeText.Decode(text, allowInvalidAmount);
}

private static ICanvas CreateCanvas(BillFormat format)
Expand Down
9 changes: 7 additions & 2 deletions Core/QRCodeText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private static string FormatAmountForCode(decimal amount)
}

private static readonly Regex ValidVersion = new Regex(@"^02\d\d$", RegexOptions.Compiled);

/// <summary>
/// Decodes the specified text and returns the bill data.
/// <para>
Expand All @@ -139,10 +139,11 @@ private static string FormatAmountForCode(decimal amount)
/// </para>
/// </summary>
/// <param name="text">The text to decode.</param>
/// <param name="allowInvalidAmount">If <c>true</c> 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 <c>null</c>.</param>
/// <returns>The decoded bill data.</returns>
/// <exception cref="QRBillValidationException">The text is in an invalid format.</exception>
[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)
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions CoreTest/CoreTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
</ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" /> </ItemGroup>

</Project>
62 changes: 62 additions & 0 deletions CoreTest/DecodedTextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<QRBillValidationException>(
() => QRBill.DecodeQrCodeText(SampleData.CreateInvalidQrCode1()));
TestHelper.AssertSingleError(err.Result, ValidationConstants.KeyNumberInvalid, ValidationConstants.FieldAmount);
}

private class NewLineTheoryData : TheoryData<Bill.QrDataSeparator, string, bool>
{
Expand Down
76 changes: 76 additions & 0 deletions CoreTest/SampleData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Collections.Generic;
using System.Numerics;
using System.Security.Principal;
using System.Text;


namespace Codecrete.SwissQRBill.CoreTest
Expand Down Expand Up @@ -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();
}
}
}