diff --git a/docs/pt-br/utilities.md b/docs/pt-br/utilities.md index b375f313..cf566730 100644 --- a/docs/pt-br/utilities.md +++ b/docs/pt-br/utilities.md @@ -84,6 +84,21 @@ import { isValidBoleto } from '@brazilian-utils/brazilian-utils'; isValidBoleto('00190000090114971860168524522114675860000102656'); // true ``` +## getBoletoInfo + +Retorna informações sobre um boleto válido. + +```javascript +import { getBoletoInfo } from '@brazilian-utils/brazilian-utils'; + +getBoletoInfo('00190000090114971860168524522114675860000102656'); +// { +// valueInCents: 102656, +// expirationDate: 2018-07-15T03:00:00.000Z, +// bankCode: '001' +// } +``` + ## isValidEmail Valida se email é valido. diff --git a/docs/utilities.md b/docs/utilities.md index 09a40031..6b2ad41d 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -28,7 +28,7 @@ formatCPF('746506880', { pad: true }); // 007.465.068-80 Generate a valid random CPF. ```javascript -import { generateCPF } from '@brazilian-utils/brazilian-utils' +import { generateCPF } from '@brazilian-utils/brazilian-utils'; generateCPF(); ``` @@ -69,7 +69,7 @@ isValidCEP('92500000'); // true Generate a valid random CNPJ. ```javascript -import { generateCNPJ } from '@brazilian-utils/brazilian-utils' +import { generateCNPJ } from '@brazilian-utils/brazilian-utils'; generateCNPJ(); ``` @@ -84,6 +84,21 @@ import { isValidBoleto } from '@brazilian-utils/brazilian-utils'; isValidBoleto('00190000090114971860168524522114675860000102656'); // true ``` +## getBoletoInfo + +Get information about a valid boleto ([brazilian payment method](https://en.wikipedia.org/wiki/Boleto)). + +```javascript +import { getBoletoInfo } from '@brazilian-utils/brazilian-utils'; + +getBoletoInfo('00190000090114971860168524522114675860000102656'); +// { +// valueInCents: 102656, +// expirationDate: 2018-07-15T03:00:00.000Z, +// bankCode: '001' +// } +``` + ## isValidEmail Check if email is valid. diff --git a/src/index.test.ts b/src/index.test.ts index f8d4baa0..26abe451 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -24,6 +24,7 @@ describe('Public API', () => { 'generateCNPJ', 'formatBoleto', 'isValidBoleto', + 'getBoletoInfo', 'generateChecksum', 'generateRandomNumber', 'getCities', diff --git a/src/utilities/boleto/index.test.ts b/src/utilities/boleto/index.test.ts index af611854..0f75dc15 100644 --- a/src/utilities/boleto/index.test.ts +++ b/src/utilities/boleto/index.test.ts @@ -1,4 +1,4 @@ -import { isValid, format, LENGTH } from '.'; +import { isValid, format, getValueInCents, getExpirationDate, getBankCode, getInfo, LENGTH } from '.'; describe('isValid', () => { describe('should return false', () => { @@ -144,3 +144,99 @@ describe('format', () => { expect(format('')).toBe(''); }); }); + +describe('getValueInCents', () => { + describe('should return zero', () => { + test('should return zero value when boleto is empty string', () => { + expect(getValueInCents('')).toBe(0); + }); + + test('should return zero value when boleto is invalid', () => { + expect(getValueInCents('00190000090114971860168524522114775860000102656')).toBe(0); + }); + }); + + describe('should return boleto value', () => { + test('should return the value in cents', () => { + expect(getValueInCents('00190000090114971860168524522114675860000102656')).toBe(102656); + }); + + test('should return value in cents when boleto is with mask', () => { + expect(getValueInCents('0019000009 01149.718601 68524.522114 6 75860000102656')).toBe(102656); + }); + }); +}); + +describe('getExpirationDate', () => { + describe('should return zero', () => { + test('should return null when boleto is empty string', () => { + expect(getExpirationDate('')).toBe(null); + }); + + test('should return null when boleto is invalid', () => { + expect(getExpirationDate('00190000090114971860168524522114775860000102656')).toBe(null); + }); + }); + + describe('should return boleto value', () => { + test('should return the expiration date', () => { + expect(getExpirationDate('00190000090114971860168524522114675860000102656')).toEqual(new Date(2018, 6, 15)); + }); + + test('should return the expiration date when boleto is with mask', () => { + expect(getExpirationDate('0019000009 01149.718601 68524.522114 6 75860000102656')).toEqual(new Date(2018, 6, 15)); + }); + }); +}); + +describe('getBankCode', () => { + describe('should return empty string', () => { + test('should return empty string when boleto is empty string', () => { + expect(getBankCode('')).toBe(''); + }); + + test('should return empty string when boleto is invalid', () => { + expect(getBankCode('00190000090114971860168524522114775860000102656')).toBe(''); + }); + }); + + describe('should return bank code value', () => { + test('should return the bank code', () => { + expect(getBankCode('00190000090114971860168524522114675860000102656')).toEqual('001'); + }); + + test('should return the bank code when boleto is with mask', () => { + expect(getBankCode('0019000009 01149.718601 68524.522114 6 75860000102656')).toEqual('001'); + }); + }); +}); + +describe('getInfo', () => { + describe('should throw error', () => { + test('should throw error when boleto is empty string', () => { + expect(() => getInfo('')).toThrow('Invalid boleto'); + }); + + test('should throw error when boleto is invalid', () => { + expect(() => getInfo('00190000090114971860168524522114775860000102656')).toThrow('Invalid boleto'); + }); + }); + + describe('should return boleto info', () => { + test('should return the bank code', () => { + expect(getInfo('00190000090114971860168524522114675860000102656')).toStrictEqual({ + valueInCents: 102656, + expirationDate: new Date(2018, 6, 15), + bankCode: '001', + }); + }); + + test('should return the bank code when boleto is with mask', () => { + expect(getInfo('0019000009 01149.718601 68524.522114 6 75860000102656')).toStrictEqual({ + valueInCents: 102656, + expirationDate: new Date(2018, 6, 15), + bankCode: '001', + }); + }); + }); +}); diff --git a/src/utilities/boleto/index.ts b/src/utilities/boleto/index.ts index 54b04053..aa3a6e63 100644 --- a/src/utilities/boleto/index.ts +++ b/src/utilities/boleto/index.ts @@ -41,6 +41,14 @@ export const DIGITABLE_LINE_TO_BOLETO_CONVERT_POSITIONS = [ { end: 31, start: 21 }, ]; +export const BANCO_CENTRAL_BASE_DATE = new Date(1997, 9, 7); + +export interface Boleto { + valueInCents: number; + expirationDate: Date | null; + bankCode: string; +} + function isValidLength(digitableLine: string): boolean { return digitableLine.length === LENGTH; } @@ -134,3 +142,44 @@ export function format(boleto: string) { return result; }, ''); } + +export function getValueInCents(digitableLine: string): number { + if (!digitableLine || !isValid(digitableLine)) return 0; + + const digits = onlyNumbers(digitableLine); + + const valueStartIndex = -10; + + return Number(digits.slice(valueStartIndex)); +} + +export function getExpirationDate(digitableLine: string): Date | null { + if (!digitableLine || !isValid(digitableLine)) return null; + + const daysSinceBaseDayStartIndex = -14; + const daysSinceBaseDayEndIndex = -10; + const daysSinceBaseDay = digitableLine.slice(daysSinceBaseDayStartIndex, daysSinceBaseDayEndIndex); + + const oneDayMilliseconds = 24 * 60 * 60 * 1000; + const millisecondsSinceBaseDay = Number(daysSinceBaseDay) * oneDayMilliseconds; + + const dateSinceBaseDay = new Date(millisecondsSinceBaseDay); + + return new Date(dateSinceBaseDay.getTime() + BANCO_CENTRAL_BASE_DATE.getTime()); +} + +export function getBankCode(digitableLine: string): string { + if (!digitableLine || !isValid(digitableLine)) return ''; + + return digitableLine.substr(0, 3); +} + +export function getInfo(digitableLine: string): Boleto { + if (!digitableLine || !isValid(digitableLine)) throw new Error('Invalid boleto'); + + return { + valueInCents: getValueInCents(digitableLine), + expirationDate: getExpirationDate(digitableLine), + bankCode: getBankCode(digitableLine), + }; +} diff --git a/src/utilities/index.ts b/src/utilities/index.ts index 48fdf9e9..fa0faa39 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -4,7 +4,7 @@ export { isValid as isValidPhone, isValidMobilePhone, isValidLandlinePhone } fro export { isValid as isValidEmail } from './email'; export { format as formatProcessoJuridico, isValid as isValidProcessoJuridico } from './processo-juridico'; export { format as formatCEP, isValid as isValidCEP } from './cep'; -export { format as formatBoleto, isValid as isValidBoleto } from './boleto'; +export { format as formatBoleto, isValid as isValidBoleto, getInfo as getBoletoInfo } from './boleto'; export { format as formatCurrency, parse as parseCurrency } from './currency'; export { format as formatCPF, generate as generateCPF, isValid as isValidCPF } from './cpf'; export { format as formatCNPJ, generate as generateCNPJ, isValid as isValidCNPJ } from './cnpj';