From 26265a8bcc67fcc53b14b20c301753476a6cc0b1 Mon Sep 17 00:00:00 2001 From: David Koblas Date: Thu, 6 Jul 2023 07:35:49 -0400 Subject: [PATCH] feat:added ME/PIB - Montenegro VAT --- README.md | 1 + src/me/index.ts | 1 + src/me/pib.spec.ts | 158 +++++++++++++++++++++++++++++++++++++++++++++ src/me/pib.ts | 77 ++++++++++++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 src/me/pib.spec.ts create mode 100644 src/me/pib.ts diff --git a/README.md b/README.md index 62480623..5c70b4c7 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ All country validators are in the "namespace" of the ISO country code. | Mexico | MX | CURP | Person | Individual Identifier (Clave Única de Registro de Población) | | Mexico | MX | CLABE | Bank | Bank Account (Clave Bancaria Estandarizada) | | Montenegro | ME | JMBG | Person | Unique Master Citizen Number | +| Montenegro | ME | PIB | Tax/Vat | Poreski Identifikacioni Broj, Montenegro tax number | | Malaysia | MY | NRIC | Person | Malaysian National Registration Identity Card Number | | Netherlands | NL | BSN | Person | Burgerservicenummer, the Dutch citizen identification number | | Netherlands | NL | BTW | Vat | Btw-identificatienummer (Omzetbelastingnummer, the Dutch VAT number) | diff --git a/src/me/index.ts b/src/me/index.ts index e17e48f6..1032d1de 100644 --- a/src/me/index.ts +++ b/src/me/index.ts @@ -1 +1,2 @@ export * as jmbg from './jmbg'; +export * as pib from './pib'; diff --git a/src/me/pib.spec.ts b/src/me/pib.spec.ts new file mode 100644 index 00000000..457a235f --- /dev/null +++ b/src/me/pib.spec.ts @@ -0,0 +1,158 @@ +import { InvalidChecksum, InvalidLength } from '../exceptions'; +import { validate, format } from './pib'; + +describe('me/pib', () => { + it('format:02000989', () => { + const result = format('02000989'); + + expect(result).toEqual('02000989'); + }); + + test.each([ + '02000989', + '02005115', + '02005328', + '02007479', + '02008599', + '02015099', + '02017105', + '02018560', + '02026325', + '02033143', + '02033356', + '02044188', + '02046954', + '02047403', + '02051664', + '02052822', + '02082390', + '02085020', + '02087723', + '02094754', + '02096064', + '02096099', + '02106183', + '02118912', + '02126265', + '02131013', + '02132419', + '02160102', + '02171058', + '02194007', + '02196727', + '02216078', + '02219603', + '02241102', + '02259974', + '02264811', + '02265435', + '02272296', + '02291266', + '02293099', + '02303213', + '02305054', + '02305623', + '02309084', + '02310783', + '02313987', + '02335450', + '02355388', + '02357950', + '02383136', + '02384337', + '02385040', + '02389231', + '02395673', + '02404281', + '02407515', + '02436159', + '02437643', + '02440768', + '02448076', + '02454190', + '02455455', + '02462494', + '02465787', + '02467593', + '02628988', + '02630419', + '02653753', + '02656515', + '02671930', + '02694638', + '02697904', + '02702967', + '02705001', + '02707942', + '02709392', + '02717557', + '02739500', + '02751372', + '02759519', + '02766515', + '02769336', + '02783746', + '02865971', + '02868474', + '02880474', + '02894998', + '02896753', + '02904870', + '02908433', + '02952165', + '02959801', + '02983303', + '03016480', + '03022480', + '03037002', + '03099873', + '03183246', + '03313468', + '03328139', + '03350479', + '03350487', + '03350495', + '03350509', + '03350517', + '03350525', + '03350533', + '03350541', + '03350550', + '03350568', + '03350576', + '03350584', + '03350592', + '03350606', + '03350614', + '03350622', + '03350665', + '03350673', + '03350681', + '03350690', + '03350703', + '03350789', + '03351483', + '03352480', + '03353486', + '03354482', + '03355489', + '03356485', + '03357481', + ])('validate:%s', value => { + const result = validate(value); + + expect(result.isValid).toEqual(true); + }); + + it('validate:0335348', () => { + const result = validate('0335348'); + + expect(result.error).toBeInstanceOf(InvalidLength); + }); + + it('validate:03353487', () => { + const result = validate('03353487'); + + expect(result.error).toBeInstanceOf(InvalidChecksum); + }); +}); diff --git a/src/me/pib.ts b/src/me/pib.ts new file mode 100644 index 00000000..9db55fe0 --- /dev/null +++ b/src/me/pib.ts @@ -0,0 +1,77 @@ +/** + * PIB (Poreski Identifikacioni Broj, Montenegro tax number). + * + * This number consists of 8 digits. + * + * Source + * http://www.pretraga.crps.me:8083/ + * https://www.vatify.eu/montenegro-vat-number.html + * + * 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 { + return strings.cleanUnicode(input, ' -'); +} + +const impl: Validator = { + name: 'Montenegro tax number', + localName: 'Poreski Identifikacioni Broj', + abbreviation: 'PIB', + + compact(input: string): string { + const [value, err] = clean(input); + + if (err) { + throw err; + } + + return value; + }, + + format(input: string): string { + const [value] = clean(input); + + return value; + }, + + validate(input: string): ValidateReturn { + const [value, error] = clean(input); + + if (error) { + return { isValid: false, error }; + } + if (value.length !== 8) { + return { isValid: false, error: new exceptions.InvalidLength() }; + } + + if (!strings.isdigits(value)) { + return { isValid: false, error: new exceptions.InvalidFormat() }; + } + + const [front, check] = strings.splitAt(value, 7); + + const sum = weightedSum(front, { + weights: [8, 7, 6, 5, 4, 3, 2], + modulus: 11, + }); + + if (String(((11 - sum) % 11) % 10) !== check) { + return { isValid: false, error: new exceptions.InvalidChecksum() }; + } + + return { + isValid: true, + compact: value, + isIndividual: false, + isCompany: true, + }; + }, +}; + +export const { name, localName, abbreviation, validate, format, compact } = + impl;