Company register number: the first two letters of anything typed are taken for a country and split off #636

Closed
opened 2026-10-01 10:48:33 +00:00 by tiagoagueda · 0 comments
Owner

Found on 2026-09-29 in a read-only review of postulo at 95348908d, running register numbers as registers print them through jobs.identifiers.clean.

Checked again on 2026-10-01 against main at 0a5cad587: still present.

A register number typed without its country code is not refused. Its first two letters are split off as if they were the country, and the altered value is saved with no message.

  • Where: plugins/identifiers/schemes.py:87-92 (_country_then_number) and plugins/identifiers/schemes.py:205-214 (the register scheme and its pattern).

  • What happens: _country_then_number inserts a space after the first two characters whenever they are letters and the third is not a space:

    if len(value) > 2 and value[:2].isalpha() and value[2] != " ":
        value = value[:2] + " " + value[2:].lstrip(" -:")
    

    It is meant for PT501234567, and it does the same to any value that starts with a word. The pattern, ^[A-Z]{2} [A-Z0-9][A-Z0-9 .\-/]{1,38}$, then accepts the result, because it asks only for two capital letters, a space and more characters; the two letters are not checked against any list of countries. Run against the snapshot:

    Typed Stored
    HRB 12345 HR B 12345
    SIRET 552 081 317 00013 SI RET 552 081 317 00013
    NIF 501234567 NI F 501234567
    KVK 12345678 KV K 12345678
    Companies House 01234567 CO MPANIES HOUSE 01234567

    XX 12 and ZZ-99 are accepted as XX 12 and ZZ 99. A bare number such as 501234567 is refused with the example, as intended.

  • Why it matters: a German number typed as it is printed is stored as one that reads as Croatian, and the person is not told. Identifiers are how two records are recognised as the same employer, so a value stored under the wrong prefix does not match the same number entered correctly elsewhere. The module's docstring says these are validated "beyond a country prefix" only; here the prefix itself is invented.

  • Fix: split only when the two letters are directly followed by a digit, or by - or : (the PT501234567 and PT-501234567 cases), and leave anything else as typed so that the pattern refuses it with the example (DE HRB 12345). Check that the first two letters are a code in the country table (phones.COUNTRIES, or an ISO 3166-1 list kept with the plugin), with a message of its own ("Start with the country's two-letter code").

Done when

  • Tests in tests/test_identifier_registry.py: HRB 12345 is refused with the example, DE HRB 12345, PT501234567 and PT-501234567 are kept as DE HRB 12345 and PT 501234567, and XX 12 is refused. The first and the last fail today.
Found on 2026-09-29 in a read-only review of postulo at 95348908d, running register numbers as registers print them through `jobs.identifiers.clean`. Checked again on 2026-10-01 against `main` at `0a5cad587`: still present. A register number typed without its country code is not refused. Its first two letters are split off as if they were the country, and the altered value is saved with no message. - **Where:** `plugins/identifiers/schemes.py:87-92` (`_country_then_number`) and `plugins/identifiers/schemes.py:205-214` (the `register` scheme and its pattern). - **What happens:** `_country_then_number` inserts a space after the first two characters whenever they are letters and the third is not a space: ```python if len(value) > 2 and value[:2].isalpha() and value[2] != " ": value = value[:2] + " " + value[2:].lstrip(" -:") ``` It is meant for `PT501234567`, and it does the same to any value that starts with a word. The pattern, `^[A-Z]{2} [A-Z0-9][A-Z0-9 .\-/]{1,38}$`, then accepts the result, because it asks only for two capital letters, a space and more characters; the two letters are not checked against any list of countries. Run against the snapshot: | Typed | Stored | | --- | --- | | `HRB 12345` | `HR B 12345` | | `SIRET 552 081 317 00013` | `SI RET 552 081 317 00013` | | `NIF 501234567` | `NI F 501234567` | | `KVK 12345678` | `KV K 12345678` | | `Companies House 01234567` | `CO MPANIES HOUSE 01234567` | `XX 12` and `ZZ-99` are accepted as `XX 12` and `ZZ 99`. A bare number such as `501234567` is refused with the example, as intended. - **Why it matters:** a German number typed as it is printed is stored as one that reads as Croatian, and the person is not told. Identifiers are how two records are recognised as the same employer, so a value stored under the wrong prefix does not match the same number entered correctly elsewhere. The module's docstring says these are validated "beyond a country prefix" only; here the prefix itself is invented. - **Fix:** split only when the two letters are directly followed by a digit, or by `-` or `:` (the `PT501234567` and `PT-501234567` cases), and leave anything else as typed so that the pattern refuses it with the example (`DE HRB 12345`). Check that the first two letters are a code in the country table (`phones.COUNTRIES`, or an ISO 3166-1 list kept with the plugin), with a message of its own ("Start with the country's two-letter code"). ## Done when - Tests in `tests/test_identifier_registry.py`: `HRB 12345` is refused with the example, `DE HRB 12345`, `PT501234567` and `PT-501234567` are kept as `DE HRB 12345` and `PT 501234567`, and `XX 12` is refused. The first and the last fail today.
tiagoagueda added this to the 0.6.0 milestone 2026-10-01 10:48:33 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#636
No description provided.