Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
name: Release

permissions:
id-token: write # Required for OIDC
contents: write

on:
#push:
# branches:
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ How you can help! This library currently support about half the countries in the
| ---------------------- | ---- | --------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| Andorra | AD | NRT | Tax | Tax Register Identifier (Número de Registre Tributari) |
| Albania | AL | NIPT | Vat | Albanian Vat Identifier (Numri i Identifikimit për Personin e Tatueshëm) |
| Anguilla | AI | TIN | Tax | Tax Identification Number |
| Argentina | AR | CBU | Bank | Single Banking Code (Clave Bancaria Uniforme) |
| Argentina | AR | CUIT | Tax | Unique Tax Identification Code (Código Único de Identificación Tributaria) |
| Argentina | AR | DNI | Person | National Identity Document (Documento Nacional de Identidad) |
Expand Down
1 change: 1 addition & 0 deletions src/ai/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as tin from './tin';
54 changes: 54 additions & 0 deletions src/ai/tin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { validate, format } from './tin';
import { InvalidLength, InvalidFormat } from '../exceptions';

describe('ai/tin', () => {
Comment thread
koblas marked this conversation as resolved.
it('format:12345-67890', () => {
const result = format('1234567890');

expect(result).toEqual('12345-67890');
});

it('validate:12345-67890', () => {
const result = validate('12345-67890');

expect(result.isValid && result.compact).toEqual('1234567890');
});

test.each([
'1234567890', // Valid format
'12345-67890', // Valid format with hyphen
])('validate:%s', value => {
const result = validate(value);

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

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

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

it('validate:3234567890', () => {
const result = validate('3234567890');
expect(result.isValid).toEqual(false);
});

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

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

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

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

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

// expect(result.error).toBeInstanceOf(InvalidChecksum);
// });
});
96 changes: 96 additions & 0 deletions src/ai/tin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* TIN (Anguilla Tax Identification Number).
*
* The Anguilla Tax Identification Number is issued to individuals and entities
* for tax purposes. The number consists of 10 digits in the format XXXXX-XXXXX
* where the first 5 digits represent the taxpayer's registration number and
* the last 5 digits are a sequence number (including prefix and check digit).
*
* TINs are unique numbers automatically generated by the tax administration system
* and consist of 10 digits, including prefix digit and check digit.
* - Individual TINs start with prefix 1
* - Non-Individual (business) TINs start with prefix 2
Comment thread
koblas marked this conversation as resolved.
*
* Source
* https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Anguilla-TIN.pdf
*
* PERSON/ENTITY
*/

import * as exceptions from '../exceptions';
import { strings } from '../util';
import { Validator, ValidateReturn } from '../types';
// import { weightedSum } from '../util/checksum';

function clean(input: string): ReturnType<typeof strings.cleanUnicode> {
return strings.cleanUnicode(input, ' -');
}

const impl: Validator = {
name: 'Anguilla Tax Identification Number',
localName: 'Tax Identification Number',
abbreviation: 'TIN',

compact(input: string): string {
const [value, err] = clean(input);

if (err) {
throw err;
}

return value;
},

format(input: string): string {
const [value] = clean(input);

if (value.length !== 10) {
return value;
Comment thread
koblas marked this conversation as resolved.
}

return `${value.substring(0, 5)}-${value.substring(5)}`;
},

validate(input: string): ValidateReturn {
const [value, error] = clean(input);

if (error) {
return { isValid: false, error };
}

if (value.length !== 10) {
return { isValid: false, error: new exceptions.InvalidLength() };
}

if (!strings.isdigits(value)) {
return { isValid: false, error: new exceptions.InvalidFormat() };
}

const prefix = value[0];

if (prefix !== '1' && prefix !== '2') {
return { isValid: false, error: new exceptions.InvalidComponent() };
}

// Validate checksum
// const [front, check] = strings.splitAt(value, 8);
// const sum = weightedSum(front, {
// weights: [7, 3, 1, 7, 3, 1, 7, 3],
// modulus: 10,
// });

// if (String(sum) !== check) {
// return { isValid: false, error: new exceptions.InvalidChecksum() };
// }
Comment thread
koblas marked this conversation as resolved.

return {
isValid: true,
compact: value,
isIndividual: prefix === '1',
isCompany: prefix === '2',
};
},
};

export const { name, localName, abbreviation, validate, format, compact } =
impl;
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as AD from './ad';
import * as AI from './ai';
import * as AL from './al';
import * as AR from './ar';
import * as AT from './at';
Expand Down Expand Up @@ -186,6 +187,7 @@ export const stdnum: Record<string, Record<string, Validator>> = {

export const personValidators: Record<string, Validator[]> = {
AD: [AD.nrt],
AI: [AI.tin],
AL: [AL.nipt],
AR: [AR.cuit, AR.dni],
AT: [AT.vnr],
Expand Down Expand Up @@ -214,6 +216,7 @@ export const personValidators: Record<string, Validator[]> = {
FI: [FI.hetu],
FR: [FR.nif, FR.nir],
GB: [GB.nino, GB.utr],
GH: [GH.tin],
Comment thread
koblas marked this conversation as resolved.
GR: [GR.amka],
GT: [GT.cui],
HK: [HK.hkid],
Expand Down Expand Up @@ -251,14 +254,17 @@ export const personValidators: Record<string, Validator[]> = {
SV: [SV.nit],
TH: [TH.idnr],
TR: [TR.tckimlik],
TW: [TW.ubn],
Comment thread
koblas marked this conversation as resolved.
UA: [UA.rntrc],
US: [US.ssn],
UY: [UY.nie, UY.cedula],
VN: [VN.mst],
Comment thread
koblas marked this conversation as resolved.
ZA: [ZA.tin, ZA.idnr],
};

export const entityValidators: Record<string, Validator[]> = {
AD: [AD.nrt],
AI: [AI.tin],
AL: [AL.nipt],
AR: [AR.cuit],
AT: [AT.businessid, AT.tin, AT.uid],
Expand Down