Swiss ql bill update for 2.3 version - #8
Open
dx-aleksei-ushakov wants to merge 1 commit into
Open
dx-aleksei-ushakov wants to merge 1 commit into
dx-aleksei-ushakov wants to merge 1 commit into
Conversation
dx-aleksei-ushakov
requested a review
from DevExpressExampleBot
as a code owner
September 16, 2026 07:31
dx-aleksei-ushakov
requested
a balanced review from Copilot
and removed request for
DevExpressExampleBot
September 16, 2026 07:31
There was a problem hiding this comment.
🟡 Changes recommended
Invalid account/reference combinations, unsafe amount clamping, and overly permissive parsing can produce or accept non-conforming payment data.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Updates Swiss QR-bill handling for guideline v2.3 with stricter validation and SWICO billing support.
Changes:
- Adds checksum, character-set, length, and address validation.
- Enforces structured addresses and valid account/reference formats.
- Adds SWICO billing-information serialization and parsing.
File summaries
| File | Description |
|---|---|
Validation.cs |
Adds field-specific exceptions. |
TestData.cs |
Replaces placeholder account and address data. |
FieldValidation.cs |
Adds character and length validation. |
Enums.cs |
Deprecates combined addresses. |
SwicoBillingInformation.cs |
Adds SWICO billing models and parsing. |
QRBillDataItem.cs |
Updates formatting and validation. |
PaymentReferenceAccountNumber.cs |
Validates reference checksums. |
CreditorAccountNumber.cs |
Validates IBAN checksums and QR-IIDs. |
AlternativeProcedures.cs |
Validates procedure lengths. |
Address.cs |
Supports textual address components and structured-only output. |
ChecksumValidator.cs |
Implements Mod10 and Mod97 validation. |
Review details
Suppressed comments (2)
CustomControls.SwissQRBill/SwissQRBill/ChecksumValidator.cs:44
- ISO 7064 conversion is defined for ASCII
0-9andA-Z, butchar.IsDigit/char.IsLetteradmit Unicode characters and the subtraction then assigns them invalid numeric values. Because the IBAN shape check does not constrain the BBAN characters, a non-ASCII account can reach this path and be treated as a checksum candidate.
if(char.IsDigit(c)) {
numericValue = c - '0';
remainder = (remainder * 10 + numericValue) % 97;
} else if(char.IsLetter(c)) {
numericValue = char.ToUpperInvariant(c) - 'A' + 10;
remainder = (remainder * 100 + numericValue) % 97;
CustomControls.SwissQRBill/SwissQRBill/TestData.cs:124
- This factory combines a standard IBAN (IID 44995) with a 27-digit QR reference. The updated
ReferenceTypelogic therefore returnsNONwhile serialization still writes the reference, producing an invalid payload and no longer matchingBillFromRealData. Use either a valid RF creditor reference with this IBAN or a valid QR-IBAN with this QR reference, and update the constant accordingly.
Reference = new PaymentReferenceAccountNumber("210000000003139471430009017"),
- Files reviewed: 11/11 changed files
- Comments generated: 9
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| }; | ||
|
|
||
| public static bool IsValidMod10Recursive(string reference) { | ||
| if(reference == null || reference.Length != 27 || !reference.All(char.IsDigit)) |
Comment on lines
+58
to
+60
| if(CreditorAccountNumber.NumberFormat == AccountNumberFormat.QR_IBAN) | ||
| return Reference.NumberFormat == AccountNumberFormat.QRReference ? ReferenceType.QRR : ReferenceType.NON; | ||
| return Reference.NumberFormat == AccountNumberFormat.CreditorReference ? ReferenceType.SCOR : ReferenceType.NON; |
Comment on lines
+134
to
+135
| if(amount.Value > MaxAmount) | ||
| return MaxAmount; |
Comment on lines
+44
to
+45
| if(!string.IsNullOrEmpty(value) && !Regex.IsMatch(value, "^[A-Za-z]{2}$")) | ||
| throw ValidationError.FieldException("Country", "Must be a two-letter country code (ISO 3166-1)."); |
| stringBuilder.AppendLine(DebtorInformation.ConvertToQRCodeDataString()); | ||
| stringBuilder.AppendLine(ReferenceType.ToString()); | ||
| stringBuilder.AppendLine(Reference == null ? string.Empty : Reference.ConvertToQRCodeDataString()); | ||
| FieldValidation.ValidateCombinedLength(AdditionalInformation, StructuredInformation, 140, "AdditionalInformation + StructuredInformation (combined)"); |
Comment on lines
+48
to
+50
| return string.IsNullOrEmpty(InvoiceNumber) && InvoiceDate == null && string.IsNullOrEmpty(CustomerReference) | ||
| && string.IsNullOrEmpty(VatNumber) && VatDate == null && VatRate == null | ||
| && VatDetails.Count == 0 && ImportVatDetails.Count == 0 && PaymentConditions.Count == 0; |
Comment on lines
+148
to
+150
| case 30: | ||
| info.VatNumber = raw; | ||
| break; |
Comment on lines
+169
to
+170
| } else if(decimal.TryParse(raw, NumberStyles.Number, CultureInfo.InvariantCulture, out rate)) { | ||
| info.VatRate = rate; |
| public const string BillWithFullSectionFieldsEmpty = "SPC\r\n0200\r\n1\r\nCH5800791123000889012\r\nS\r\nSchreinerei Habegger & Sohne\r\nUetlibergstrasse\r\n138\r\n8045\r\nZuric\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nSCOR\r\nRF18539007547034\r\n\r\nEPD\r\n"; | ||
| public const string BillWithTwoProcedures = "SPC\r\n0200\r\n1\r\nCH5800791123000889012\r\nS\r\nSchreinerei Habegger & Sohne\r\nUetlibergstrasse\r\n138\r\n8045\r\nZuric\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n287.30\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nSCOR\r\nRF18539007547034\r\nRechnungsnr. 10978 / Auftragsrnr. 3987\r\nEPD\r\n//bill information\r\nName AV1: UV;UltraPay005;12345\r\nName AV2: XY;XYService;54321"; | ||
| public const string BillWithOneProcedure = "SPC\r\n0200\r\n1\r\nCH5800791123000889012\r\nS\r\nSchreinerei Habegger & Sohne\r\nUetlibergstrasse\r\n138\r\n8045\r\nZuric\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n287.30\r\nEUR\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nSCOR\r\nRF18539007547034\r\nRechnungsnr. 10978 / Auftragsrnr. 3987\r\nEPD\r\n//bill information\r\nName AV1: XY;XYService;54321"; | ||
| public const string BillFromRealData = "SPC\r\n0200\r\n1\r\nCH4944995599000899901\r\nS\r\nHenri Schmid Service Switzerland AG\r\nMuseumstrasse\r\n258\r\n2501\r\nBiel\r\nCH\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n8690.00\r\nCHF\r\nS\r\nPia-Maria Rutschmann-Schnyder\r\nGrosse Marktgasse\r\n28\r\n9400\r\nRorschach\r\nCH\r\nSCOR\r\n210000000003139471430009017\r\nAuftrag vom 25.10.2019##S1/01/20170309/11/10201409/20/14000/22/36958/30/CH10646546/40/1020/41/3010\r\nEPD\r\n"; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.