Skip to content
This repository was archived by the owner on Apr 12, 2025. It is now read-only.

Commit 93f86c5

Browse files
JoaoFGuiomarimhugofonseca
authored andcommitted
Added isValidCPECUI andd formatToCurrency (#27)
1 parent 8ef5c50 commit 93f86c5

File tree

9 files changed

+217
-18
lines changed

9 files changed

+217
-18
lines changed

docs/_sidebar.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
* Documentation
77
* Formaters
88
* [formatDecimalNumber](formaters?id=formatdecimalnumbernumber-stringnumber-options-object)
9+
* [formatToCurrency](formaters?id=formattocurrencynumber-number-numdecimals-number)
910
* Validators
1011
* [isValidNIF](validators.md?id=isvalidnifnif-string)
1112
* [isValidPostalCode](validators.md?id=isvalidpostalcodepostalcode-string)
1213
* [isValidMobileNumber](validators.md?id=isvalidmobilenumbermobilenumber-string)
1314
* [isValidCCNumber](validators.md?id=isvalidccnumbercitizencardnumber-string)
1415
* [isValidLicensePlate](validators.md?id=isvalidlicenseplateplatenumber-string)
16+
* [isValidCPECUI](validators.md?id=isvalidcpecui-string)

docs/formaters.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,41 @@ formatedNumber = formatDecimalNumber(300.128, {
6161
console.log(formatedNumber)
6262
// => '300,128'
6363
```
64+
65+
## formatToCurrency(number: number, numDecimals: number)
66+
67+
Formats numbers (integer or floating) portugal's currency
68+
69+
#### Arguments
70+
71+
**number (number)**: The number to be formated
72+
73+
**numDecimals (number)**: Option for number of decimals
74+
75+
> _default: 0_
76+
77+
#### Returns
78+
79+
**_(string)_**: The formated number
80+
81+
#### Example
82+
83+
```js
84+
import formatToCurrency from 'portuguese-utils/formaters/formatToCurrency'
85+
86+
// Default options
87+
let formatedNumber = formatToCurrency('3000.134')
88+
89+
console.log(formatedNumber)
90+
// => '3.00 €'
91+
92+
formatedNumber = formatToCurrency(300.5, 2)
93+
94+
console.log(formatedNumber)
95+
// => '300.50 €'
96+
97+
formatedNumber = formatToCurrency(1234567.9, 2)
98+
99+
console.log(formatedNumber)
100+
// => '1.234.567,90 €'
101+
```

docs/validators.md

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
# Validators
22

33
## isValidNIF(nif: string)
4-
Validates a portuguese fiscal number
54

5+
Validates a portuguese fiscal number
66

77
#### Arguments
8-
***nif (string)*** : The fiscal number to be checked as string.
98

9+
**_nif (string)_** : The fiscal number to be checked as string.
1010

1111
#### Returns
12-
***(boolean)***: True if valid and False if invalid.
1312

13+
**_(boolean)_**: True if valid and False if invalid.
1414

1515
#### Example
1616

@@ -23,16 +23,16 @@ console.log(isNIFValid)
2323
```
2424

2525
## isValidPostalCode(postalCode: string)
26-
Validates a portuguese postal-code
2726

27+
Validates a portuguese postal-code
2828

2929
#### Arguments
30-
***postalCode (string)*** : The postal code to be checked as string.
3130

31+
**_postalCode (string)_** : The postal code to be checked as string.
3232

3333
#### Returns
34-
***(boolean)***: True if valid and False if invalid.
3534

35+
**_(boolean)_**: True if valid and False if invalid.
3636

3737
#### Example
3838

@@ -43,17 +43,18 @@ const isCodeValid = isValidPostalCode('3865-134')
4343
console.log(isCodeValid)
4444
// => true
4545
```
46+
4647
## isValidMobileNumber(mobileNumber: string)
47-
Validates a portuguese mobile number
4848

49+
Validates a portuguese mobile number
4950

5051
#### Arguments
51-
***mobileNumber (string)*** : The mobile number to be checked as string.
5252

53+
**_mobileNumber (string)_** : The mobile number to be checked as string.
5354

5455
#### Returns
55-
***(boolean)***: True if valid and False if invalid.
5656

57+
**_(boolean)_**: True if valid and False if invalid.
5758

5859
#### Example
5960

@@ -66,16 +67,16 @@ console.log(isNumberValid)
6667
```
6768

6869
## isValidCCNumber(citizenCardNumber: string)
69-
Validates a portuguese citizen card number
7070

71+
Validates a portuguese citizen card number
7172

7273
#### Arguments
73-
***citizenCardNumber (string)*** : The citizen card number to be checked as string.
7474

75+
**_citizenCardNumber (string)_** : The citizen card number to be checked as string.
7576

7677
#### Returns
77-
***(boolean)***: True if valid and False if invalid.
7878

79+
**_(boolean)_**: True if valid and False if invalid.
7980

8081
#### Example
8182

@@ -88,16 +89,16 @@ console.log(isCCValid)
8889
```
8990

9091
## isValidLicensePlate(plateNumber: string)
91-
Validates a portuguese license plate number
9292

93+
Validates a portuguese license plate number
9394

9495
#### Arguments
95-
***plateNumber (string)*** : The license plate number checked as string.
9696

97+
**_plateNumber (string)_** : The license plate number checked as string.
9798

9899
#### Returns
99-
***(boolean)***: True if valid and False if invalid.
100100

101+
**_(boolean)_**: True if valid and False if invalid.
101102

102103
#### Example
103104

@@ -107,4 +108,29 @@ const isLicensePlateValid = isValidLicensePlate('00-AA-00')
107108

108109
console.log(isLicensePlateValid)
109110
// => true
110-
```
111+
```
112+
113+
## isValidCPECUI(cpe: string)
114+
115+
Validates CPE and CUI
116+
117+
* CPE - Código de ponto de entrega
118+
* CUI - Código universal da instalação
119+
120+
#### Arguments
121+
122+
**_cpe (string)_** : the CPE or CUI.
123+
124+
#### Returns
125+
126+
**_(boolean)_**: True if valid and False if invalid.
127+
128+
#### Example
129+
130+
```js
131+
import isValidCPECUI from 'portuguese-utils/validators/isValidCPECUI'
132+
const isValid = isValidCPECUI('PT 0014 483920194837 DW')
133+
134+
console.log(isValid)
135+
// => true
136+
```
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import formatToCurrency from 'formaters/formatToCurrency'
2+
3+
test('formatToCurrency throws an error for invalid types', () => {
4+
expect(() => formatToCurrency(undefined)).toThrow(TypeError)
5+
expect(() => formatToCurrency('12345')).toThrow(TypeError)
6+
})
7+
8+
test('formatToCurrency should return formatted number as currency string', () => {
9+
expect(formatToCurrency(1234)).toBe('1 234 €')
10+
expect(formatToCurrency(1, 2)).toBe('1,00 €')
11+
expect(formatToCurrency(123)).toBe('123 €')
12+
expect(formatToCurrency(1234, 2)).toBe('1 234,00 €')
13+
expect(formatToCurrency(1234.5, 1)).toBe('1 234,5 €')
14+
expect(formatToCurrency(1234.56, 2)).toBe('1 234,56 €')
15+
expect(formatToCurrency(1234.563123)).toBe('1 234 €')
16+
expect(formatToCurrency(1234.563123, 6)).toBe('1 234,563123 €')
17+
expect(formatToCurrency(1234567.9, 2)).toBe('1 234 567,90 €')
18+
})

src/formaters/formatToCurrency.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
*
3+
* Formats number (integer or float) to currency
4+
* @param {number} number
5+
*
6+
**/
7+
8+
export default function formatToCurrency(number, numDecimals) {
9+
if (typeof number !== 'number') {
10+
throw new TypeError('Argument number must be of type number')
11+
}
12+
13+
let integer
14+
let decimal
15+
let output
16+
let string = numDecimals
17+
? number.toFixed(numDecimals)
18+
: Math.floor(number).toString()
19+
20+
if (string.indexOf('.') !== -1) {
21+
let split = string.split('.')
22+
decimal = split[1]
23+
integer = addSeparators(split[0])
24+
output = `${integer},${decimal}`
25+
} else {
26+
integer = addSeparators(string)
27+
output = integer
28+
}
29+
30+
output = `${output} €`
31+
return output
32+
}
33+
34+
/**
35+
* Helper function
36+
* Returns a number with thousands separator
37+
+ Example: 1000000 => 1.000.000
38+
* @param {string} number
39+
**/
40+
41+
function addSeparators(number) {
42+
if (typeof number !== 'string' || isNaN(parseInt(number))) {
43+
throw new TypeError('Argument number must be digits of type String')
44+
}
45+
46+
if (number.indexOf('.') !== -1) {
47+
throw new TypeError('Argument must be an integer of type String')
48+
}
49+
50+
let count = 0
51+
52+
return number
53+
.split('')
54+
.reverse()
55+
.reduce((end, digit, index) => {
56+
end.push(index && index % 3 === 0 ? `${digit} ` : digit)
57+
return end
58+
}, [])
59+
.reverse()
60+
.join('')
61+
}

src/formaters/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import formatDecimalNumber from './formatDecimalNumber'
2+
import formatToCurrency from './formatToCurrency'
23

3-
export { formatDecimalNumber }
4+
export { formatDecimalNumber, formatToCurrency }
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import isValidCPECUI from 'validators/isValidCPECUI'
2+
3+
test('isValidCPECUI returns false for invalid number', () => {
4+
expect(isValidCPECUI(undefined)).toBe(false)
5+
expect(isValidCPECUI(1234)).toBe(false)
6+
expect(isValidCPECUI('PT 0014 483920194837 DA')).toBe(false)
7+
expect(isValidCPECUI('pt001448392019483da')).toBe(false)
8+
expect(isValidCPECUI('P&0014483920194837DA')).toBe(false)
9+
})
10+
11+
test('isValidCPECUI returns true if valid number', () => {
12+
expect(isValidCPECUI('PT 0014 483920194837 DW')).toBe(true) // CPE
13+
expect(isValidCPECUI('pt0014483920194837dw')).toBe(true) // CPE
14+
expect(isValidCPECUI('PT 1234 000004614012 QQ')).toBe(true) // CUI
15+
expect(isValidCPECUI('pt1234000004614012qq')).toBe(true) // CUI
16+
})

src/validators/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,13 @@ import isValidNIF from './isValidNIF'
33
import isValidMobileNumber from './isValidMobileNumber'
44
import isValidCCNumber from './isValidCCNumber'
55
import isValidLicensePlate from './isValidLicensePlate'
6+
import isValidCPECUI from './isValidCPECUI'
67

7-
export { isValidPostalCode, isValidNIF, isValidMobileNumber, isValidCCNumber, isValidLicensePlate }
8+
export {
9+
isValidPostalCode,
10+
isValidNIF,
11+
isValidMobileNumber,
12+
isValidCCNumber,
13+
isValidLicensePlate,
14+
isValidCPECUI
15+
}

src/validators/isValidCPECUI.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Validates if CPE (Código Ponto de Entrega) is in the valid format
3+
* @param {String} cpe
4+
*/
5+
6+
export default function isValidCPECUI(cpe) {
7+
const ABValues = 'TRWAGMYFPDXBNJZSQVHLCKE'
8+
9+
// Must be a string
10+
if (typeof cpe !== 'string') return false
11+
12+
// Validate with a pattern
13+
// Rules: PT(4digits)(12digits)(2Characters)
14+
const parsed = cpe.replace(/\s+/g, '').toUpperCase()
15+
const pattern = /PT(\d{16})([A-Z]{2})/g
16+
if (!pattern.test(parsed)) return false
17+
18+
// Calculate verification characters
19+
// Documentation:
20+
// https://bit.ly/2rARRWN
21+
const digits = Number(parsed.replace(/[A-Z]/g, ''))
22+
const remainder = digits % 529
23+
const A = ABValues[Math.floor(remainder / 23)]
24+
const B = ABValues[Math.floor(remainder % 23)]
25+
26+
// A and B must be in the CPE
27+
const lastTwoCharacters = parsed.substring(parsed.length, parsed.length - 2)
28+
return lastTwoCharacters === `${A}${B}`
29+
}

0 commit comments

Comments
 (0)