diff --git a/README.md b/README.md index 886712dd7..5d8cced02 100644 --- a/README.md +++ b/README.md @@ -884,6 +884,7 @@ isBoolean(value); | `@IsSurrogatePair()` | Checks if the string contains any surrogate pairs chars. | | `@IsTaxId()` | Checks if the string is a valid tax ID. Default locale is `en-US`. | | `@IsUrl(options?: IsURLOptions)` | Checks if the string is a URL. | +| `@IsVatId(countryCode: string)` | Checks if the string is a [valid VAT identification number for the country](https://taxation-customs.ec.europa.eu/vat-identification-numbers_en). | | `@IsMagnetURI()` | Checks if the string is a [magnet uri format](https://en.wikipedia.org/wiki/Magnet_URI_scheme). | | `@IsUUID(version?: UUIDVersion)` | Checks if the string is a UUID (version 3, 4, 5 or all ). | | `@IsFirebasePushId()` | Checks if the string is a [Firebase Push ID](https://firebase.googleblog.com/2015/02/the-2120-ways-to-ensure-unique_68.html) | diff --git a/src/decorator/decorators.ts b/src/decorator/decorators.ts index d449e9301..4f57599e8 100644 --- a/src/decorator/decorators.ts +++ b/src/decorator/decorators.ts @@ -116,6 +116,7 @@ export * from './string/IsTimeZone'; export * from './string/IsBase58'; export * from './string/is-tax-id'; export * from './string/is-iso4217-currency-code'; +export * from './string/IsVatId'; // ------------------------------------------------------------------------- // Type checkers diff --git a/src/decorator/string/IsVatId.ts b/src/decorator/string/IsVatId.ts new file mode 100644 index 000000000..72429f22b --- /dev/null +++ b/src/decorator/string/IsVatId.ts @@ -0,0 +1,36 @@ +import { ValidationOptions } from '../ValidationOptions'; +import { buildMessage, ValidateBy } from '../common/ValidateBy'; +import isVatIdValidator from 'validator/lib/isVat'; + +export const IS_VAT_ID = 'isVat'; + +/** + * Checks if the string is a VAT (value-added tax) identification number. + * If given value is not a string, then it returns false. + */ +export function isVatId(value: unknown, countryCode: string): boolean { + try { + const isVatId = typeof value === 'string' && isVatIdValidator(value, countryCode); + return isVatId; + } catch (error) { + return false; + } +} + +/** + * Checks if the string is a VAT (value-added tax) identification number. + * If given value is not a string, then it returns false. + */ +export function IsVatId(countryCode?: string, validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: IS_VAT_ID, + constraints: [countryCode], + validator: { + validate: (value, args): boolean => isVatId(value, args?.constraints[0]), + defaultMessage: buildMessage(eachPrefix => eachPrefix + '$property must be a VAT ID', validationOptions), + }, + }, + validationOptions + ); +} diff --git a/test/functional/validation-functions-and-decorators.spec.ts b/test/functional/validation-functions-and-decorators.spec.ts index 9f938616c..48069a447 100644 --- a/test/functional/validation-functions-and-decorators.spec.ts +++ b/test/functional/validation-functions-and-decorators.spec.ts @@ -193,6 +193,7 @@ import { isTaxId, IsTaxId, IsISO4217CurrencyCode, + IsVatId, } from '../../src/decorator/decorators'; import { Validator } from '../../src/validation/Validator'; import { ValidatorOptions } from '../../src/validation/ValidatorOptions'; @@ -4789,3 +4790,34 @@ describe('IsISO4217', () => { return checkInvalidValues(new MyClass(), invalidValues); }); }); + +describe('IsVatId', () => { + class MyClass { + @IsVatId('AT') + someProperty: string; + } + + class MyInvalidClass { + @IsVatId('ZZ') + someProperty: string; + } + + const validAtVatIds = ['U12345678', 'U01234567']; + const invalidAtVatIds = ['12345678', '000', 'abcdefgh']; + + it('should not fail for a valid VAT id for valid country code', () => { + return checkValidValues(new MyClass(), validAtVatIds); + }); + + it('should fail for invalid country code', () => { + return checkInvalidValues(new MyInvalidClass(), validAtVatIds); + }); + + it('should fail for invalid VAT id for valid country code', () => { + return checkInvalidValues(new MyClass(), invalidAtVatIds); + }); + + it('should fail for invalid VAT id for invalid country code', () => { + return checkInvalidValues(new MyInvalidClass(), invalidAtVatIds); + }); +});