Summary
Allow the application to determine the type of entity associated with a Portuguese Tax Identification Number (NIF).
Problem statement
Currently, it is only possible to verify whether a NIF is structurally valid. There is no way to determine what kind of entity it represents, such as an individual taxpayer, a company, a public body, or a fund, which limits the ability to apply entity-specific business rules downstream.
Proposed solution
Implement classification of NIFs by entity type, based on the ranges defined by Portuguese tax legislation (Decreto-Lei n.º 14/2013).
NIF Types
The following entity types must be represented as a NifType enum:
| Type |
Description |
INDIVIDUAL |
Individual taxpayer, resident or non-resident (prefixes: 1, 2, 3) |
NON_RESIDENT_INDIVIDUAL_WITHHOLDING |
Non-resident individual with income subject to definitive withholding tax (prefix: 45) |
LEGAL_ENTITY |
Legal entity registered with the RNPC (prefix: 5) |
PUBLIC_ADMINISTRATION |
Central, regional, or local public administration body (prefix: 6) |
UNDIVIDED_ESTATE |
Undivided estate — herança indivisa (prefixes: 70, 74, 75) |
NON_RESIDENT_LEGAL_ENTITY_WITHHOLDING |
Non-resident legal entity with income subject to definitive withholding tax (prefix: 71) |
INVESTMENT_AND_PENSION_FUNDS |
Investment funds and pension funds (prefix: 72) |
EX_OFFICIO_ASSIGNMENT |
NIF assigned ex officio to entities not required to register with the RNPC (prefix: 77) |
NON_RESIDENT_VAT_REFUND |
Non-resident entity covered by the EU VAT Refund procedure (prefix: 78) |
EXCEPTIONAL_REGIME_EXPO98 |
Exceptional regime — Expo 98 (prefix: 79) |
CONDOMINIUMS_AND_IRREGULAR_ENTITIES |
Condominiums, irregular partnerships, or undivided estates where the deceased was a sole trader (prefixes: 90, 91) |
NON_RESIDENT_NO_ESTABLISHMENT |
Non-resident entity without a permanent establishment in Portugal (prefix: 98) |
CIVIL_PARTNERSHIP_NO_LEGAL_PERSONALITY |
Civil partnership without legal personality (prefix: 99) |
Classification API
Two classification methods must be provided, sharing the same core logic:
classify — for callers that expect classification to sometimes yield no result. Returns Optional.of(type) if classification succeeds, otherwise Optional.empty(). Never fails.
classifyOrThrow — for callers that require a result and treat an unclassifiable NIF as an error. Returns the matched type if classification succeeds, otherwise throws an IllegalArgumentException.
Both invalid NIFs and unrecognized prefixes are treated uniformly as unclassifiable — neither case is distinguished from the other.
Both methods must:
- Reuse the existing
isNif validation logic for structural validation.
- Operate on a normalized NIF string (trimmed, no whitespace), consistent with how
isNif handles input.
- Apply prefix matching using the longest matching prefix first (e.g.,
45 before 4, 70 before 7). The mapping may be implemented as a prefix lookup table ordered by descending prefix length.
- Base prefix matching on the string representation of the NIF, not numeric parsing, to avoid losing leading digits.
Legal source: Decreto-Lei n.º 14/2013, of 28 January.
Available at: https://diariodarepublica.pt/dr/detalhe/decreto-lei/14-2013-257001
Acceptance Criteria
- Given a valid NIF with a known prefix, both
classify and classifyOrThrow return the correct NifType.
- Given a valid NIF with an unrecognized prefix,
classify returns Optional.empty() and classifyOrThrow throws an IllegalArgumentException.
- Given a structurally invalid NIF,
classify returns Optional.empty() and classifyOrThrow throws an IllegalArgumentException.
- Given a NIF with leading or trailing whitespace, both methods normalize the input before classifying.
- All 13 entity types defined in Decreto-Lei n.º 14/2013 are supported.
- Given a NIF whose prefix overlaps with a shorter one, the longest match wins (e.g.,
45xxxxxxx resolves to NON_RESIDENT_INDIVIDUAL_WITHHOLDING, not INDIVIDUAL).
Alternatives considered
- Providing only one classification method — rejected in favor of offering both a safe and a strict variant, to suit different calling contexts.
- Treating invalid NIFs and unrecognized prefixes differently — rejected to keep the API surface simple and uniform.
Additional context
Official sources consulted to define the NIF type ranges:
Code of Conduct
Summary
Allow the application to determine the type of entity associated with a Portuguese Tax Identification Number (NIF).
Problem statement
Currently, it is only possible to verify whether a NIF is structurally valid. There is no way to determine what kind of entity it represents, such as an individual taxpayer, a company, a public body, or a fund, which limits the ability to apply entity-specific business rules downstream.
Proposed solution
Implement classification of NIFs by entity type, based on the ranges defined by Portuguese tax legislation (Decreto-Lei n.º 14/2013).
NIF Types
The following entity types must be represented as a
NifTypeenum:INDIVIDUALNON_RESIDENT_INDIVIDUAL_WITHHOLDINGLEGAL_ENTITYPUBLIC_ADMINISTRATIONUNDIVIDED_ESTATENON_RESIDENT_LEGAL_ENTITY_WITHHOLDINGINVESTMENT_AND_PENSION_FUNDSEX_OFFICIO_ASSIGNMENTNON_RESIDENT_VAT_REFUNDEXCEPTIONAL_REGIME_EXPO98CONDOMINIUMS_AND_IRREGULAR_ENTITIESNON_RESIDENT_NO_ESTABLISHMENTCIVIL_PARTNERSHIP_NO_LEGAL_PERSONALITYClassification API
Two classification methods must be provided, sharing the same core logic:
classify— for callers that expect classification to sometimes yield no result. ReturnsOptional.of(type)if classification succeeds, otherwiseOptional.empty(). Never fails.classifyOrThrow— for callers that require a result and treat an unclassifiable NIF as an error. Returns the matched type if classification succeeds, otherwise throws anIllegalArgumentException.Both invalid NIFs and unrecognized prefixes are treated uniformly as unclassifiable — neither case is distinguished from the other.
Both methods must:
isNifvalidation logic for structural validation.isNifhandles input.45before4,70before7). The mapping may be implemented as a prefix lookup table ordered by descending prefix length.Legal source: Decreto-Lei n.º 14/2013, of 28 January.
Available at: https://diariodarepublica.pt/dr/detalhe/decreto-lei/14-2013-257001
Acceptance Criteria
classifyandclassifyOrThrowreturn the correctNifType.classifyreturnsOptional.empty()andclassifyOrThrowthrows anIllegalArgumentException.classifyreturnsOptional.empty()andclassifyOrThrowthrows anIllegalArgumentException.45xxxxxxxresolves toNON_RESIDENT_INDIVIDUAL_WITHHOLDING, notINDIVIDUAL).Alternatives considered
Additional context
Official sources consulted to define the NIF type ranges:
Code of Conduct