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
42 changes: 42 additions & 0 deletions src/cz/dic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,46 @@ describe('cz/dic', () => {

expect(result.error).toBeInstanceOf(InvalidChecksum);
});

// Special 9-digit case (DIČ starting with 6), see issue #164.
// These are valid per the official EU VIES service but were falsely
// rejected because checkSpecial diverged from python-stdnum.
it('validate:CZ687836437', () => {
const result = validate('CZ687836437');

expect(result.isValid).toBe(true);
});

it('validate:640903926', () => {
const result = validate('640903926');

expect(result.isValid).toBe(true);
});

it('validate:681208919', () => {
const result = validate('681208919');

expect(result.isValid).toBe(true);
});

// VAT-group ID (format 699nnnnnk).
it('validate:699005400', () => {
const result = validate('699005400');

expect(result.isValid).toBe(true);
});

// Wrong check digit on the special case must still be rejected.
it('validate:687836438', () => {
const result = validate('687836438');

expect(result.error).toBeInstanceOf(InvalidChecksum);
});

// Normal (non-special) 9-digit DIČ still delegates to rc and validates.
it('validate:CZ991231123', () => {
const result = validate('CZ991231123');

expect(result.isValid && result.compact).toEqual('991231123');
});
});
19 changes: 12 additions & 7 deletions src/cz/dic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import * as exceptions from '../exceptions';
import { strings, weightedSum } from '../util';
import { pymod } from '../util/pymod';
import { Validator, ValidateReturn } from '../types';
import { validate as rcValidate } from './rc';

Expand Down Expand Up @@ -41,17 +42,21 @@ function checkLegal(value: string): boolean {
}

function checkSpecial(value: string): boolean {
// check = sum((8 - i) * int(n) for i, n in enumerate(number)) % 11

// return str((8 - (10 - check) % 11) % 10)

const [front, check] = strings.splitAt(value, -1);
// Reference: python-stdnum cz/dic.py::calc_check_digit_special
// check = sum((8 - i) * int(n) for i, n in enumerate(number[1:8])) % 11
// return str((8 - (10 - check) % 11) % 10)
//
// The weighted sum runs over digits 2..8 only: the leading 6 and the check
// digit are excluded (weights [8, 7, 6, 5, 4, 3, 2]). The final reduction
// relies on Python's always-non-negative modulo, so pymod is used to match
// it -- JS '%' keeps the dividend's sign and would yield a negative digit.
const [, front, check] = strings.splitAt(value, 1, -1);

const sum = weightedSum(front, {
modulus: 11,
weights: [8, 7, 6, 5, 4, 3, 2, 1],
weights: [8, 7, 6, 5, 4, 3, 2],
});
const digit = String((8 - ((10 - sum) % 11)) % 10);
const digit = String(pymod(8 - ((10 - sum) % 11), 10));

return digit === check;
}
Expand Down