Skip to content

Comments

fix: calculation of the check digit when it is in the optional part#66

Merged
stropitek merged 2 commits intocheminfo:mainfrom
uwolfer:parseDocumentNumberCheckDigit
Jan 5, 2026
Merged

fix: calculation of the check digit when it is in the optional part#66
stropitek merged 2 commits intocheminfo:mainfrom
uwolfer:parseDocumentNumberCheckDigit

Conversation

@uwolfer
Copy link
Contributor

@uwolfer uwolfer commented Dec 29, 2025

Based on examples from PRADO and ICAO examples (https://www.icao.int/sites/default/files/publications/DocSeries/9303_p11_cons_en.pdf), the < separator before the optional part should not be included in checksum calculation.

This also resolved #36.

@codecov
Copy link

codecov bot commented Dec 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.66%. Comparing base (72943aa) to head (1912afe).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #66   +/-   ##
=======================================
  Coverage   88.66%   88.66%           
=======================================
  Files          48       48           
  Lines         450      450           
  Branches      123      123           
=======================================
  Hits          399      399           
  Misses         51       51           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@uwolfer
Copy link
Contributor Author

uwolfer commented Dec 29, 2025

@targos I think we should be very careful with this change. It increases the number of accepted check digits (potential false positives). I would appreciate if we could find an official spec which somehow confirms / explains this.

@uwolfer
Copy link
Contributor Author

uwolfer commented Dec 29, 2025

We should also double check the existing test cases which would fail without including the < in checksum calculation: are those real cases or generated ones? If they are generated and cannot be found in real, we could implement it in a more stable way.

Related: #36

@uwolfer uwolfer mentioned this pull request Dec 30, 2025
@uwolfer
Copy link
Contributor Author

uwolfer commented Dec 30, 2025

I have pushed more test cases.

If we agree that the current impl is wrong, I would apply the following to this PR:

Subject: [PATCH] fix: add support for parsing the document number check digit in more cases

Some recent Belgian and Portuguese ID cards do not include the < before the optional 1 field in the check digit.

I have not found this behavior documented in any official specification so far, but it can be confirmed using documents available online (for example, PRADO). I have also encountered real IDs with this characteristic.

diff --git a/src/parse/__tests__/td1.test.ts b/src/parse/__tests__/td1.test.ts
--- a/src/parse/__tests__/td1.test.ts	(revision d7d8376abfecde7c4b172a0d13f0b571b6dae9eb)
+++ b/src/parse/__tests__/td1.test.ts	(date 1767080209393)
@@ -291,8 +291,8 @@
 
   it('parse document number', () => {
     const MRZ = [
-      'I<UTOD23145890<1240<XYZ<<<<<<<',
-      '7408122F1204159UTO<<<<<<<<<<<8',
+      'I<UTOD23145890<1244<<<<<<<<<<<',
+      '7408122F1204159UTO<<<<<<<<<<<2',
       'ERIKSSON<<ANNA<MARIA<<<<<<<<<<',
     ];
 
@@ -318,7 +318,7 @@
       ranges: [
         { line: 0, start: 5, end: 14, raw: 'D23145890' },
         { line: 0, start: 14, end: 15, raw: '<' },
-        { line: 0, start: 15, end: 30, raw: '1240<XYZ<<<<<<<' },
+        { line: 0, start: 15, end: 30, raw: '1244<<<<<<<<<<<' },
       ],
       line: 0,
       start: 5,
@@ -326,7 +326,7 @@
       autocorrect: [],
     });
     expect(result.fields.documentNumber).toBe('D23145890124');
-    expect(result.fields.documentNumberCheckDigit).toBe('0');
+    expect(result.fields.documentNumberCheckDigit).toBe('4');
 
     const documentNumberCheckDigitDetails = result.details.find(
       (d) => d.field === 'documentNumberCheckDigit',
@@ -336,7 +336,7 @@
       line: 0,
       start: 18,
       end: 19,
-      value: '0',
+      value: '4',
     });
   });
 
Index: src/parsers/parseDocumentNumberCheckDigit.ts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/parsers/parseDocumentNumberCheckDigit.ts b/src/parsers/parseDocumentNumberCheckDigit.ts
--- a/src/parsers/parseDocumentNumberCheckDigit.ts	(revision d7d8376abfecde7c4b172a0d13f0b571b6dae9eb)
+++ b/src/parsers/parseDocumentNumberCheckDigit.ts	(date 1767080190539)
@@ -8,23 +8,14 @@
   if (checkDigit === '<' && optional) {
     const firstFiller = optional.indexOf('<');
     const tail = optional.slice(0, firstFiller - 1);
+    source = `${source}${tail}`;
     checkDigit = optional.charAt(firstFiller - 1);
-    try {
-      check(`${source}<${tail}`, checkDigit);
-      return {
-        value: checkDigit,
-        start: firstFiller,
-        end: firstFiller + 1,
-      };
-    } catch (error) {
-      check(`${source}${tail}`, checkDigit);
-      return {
-        error: error.message,
-        value: checkDigit,
-        start: firstFiller,
-        end: firstFiller + 1,
-      };
-    }
+    check(source, checkDigit);
+    return {
+      value: checkDigit,
+      start: firstFiller,
+      end: firstFiller + 1,
+    };
   } else {
     check(source, checkDigit);
     return checkDigit;

Copy link
Contributor

@stropitek stropitek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the implementation which tries 2 different check digit implementations and marks it as valid if any of those pass is not ok.

The already existing test which had a new check digit introduced here and changed the parsing implementation: 5166825

Given it changed the existing test but added a dummy additional optional part + new check digit, I assume this is not a PRADO example anymore.

Therefore I think your implementation fixes the calculation of the check digit when it is in the optional part. We can keep only that one and mark it as a fix rather than a breaking change.

@uwolfer uwolfer changed the title fix: add support for parsing the document number check digit in more cases fix: calculation of the check digit when it is in the optional part Jan 5, 2026
@uwolfer uwolfer force-pushed the parseDocumentNumberCheckDigit branch from e87b31c to 367e39e Compare January 5, 2026 15:17
Based on examples from PRADO and ICAO examples (https://www.icao.int/sites/default/files/publications/DocSeries/9303_p11_cons_en.pdf), the `<` separator before the optional part should not be included in checksum calculation.

This also resolved cheminfo#36.
@uwolfer uwolfer force-pushed the parseDocumentNumberCheckDigit branch from 367e39e to e2f7e33 Compare January 5, 2026 15:20
@uwolfer
Copy link
Contributor Author

uwolfer commented Jan 5, 2026

@stropitek Thanks for you feedback. I have applied it and also updated the initial comment and PR title accordingly.

I have synced the test case and the README sample.

@stropitek
Copy link
Contributor

For reference, this was probably the reference document we used at the time as there are similar examples.

It includes examples where the document number check digit is valid only with your fix, so it all SGTM.

https://www.icao.int/sites/default/files/publications/DocSeries/9303_p11_cons_en.pdf

Same document as attachment:

9303_p11_cons_en.pdf

Copy link
Contributor

@stropitek stropitek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the contribution!

@stropitek stropitek merged commit 4ae81df into cheminfo:main Jan 5, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid MRZ

2 participants