diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 5121faa4..0e3277b1 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -2,3 +2,4 @@ export { isLastChar } from './is-last-char'; export { onlyNumbers } from './only-numbers'; export { generateChecksum } from './generate-checksum'; export { generateRandomNumber } from './generate-random-number'; +export { isEven } from './is-even'; diff --git a/src/helpers/is-even/index.ts b/src/helpers/is-even/index.ts new file mode 100644 index 00000000..5348cafd --- /dev/null +++ b/src/helpers/is-even/index.ts @@ -0,0 +1,3 @@ +export function isEven(number: number): boolean { + return number % 2 === 0; +} diff --git a/src/utilities/conta-bancaria/__tests__/bancoBanrisul.test.ts b/src/utilities/conta-bancaria/__tests__/bancoBanrisul.test.ts new file mode 100644 index 00000000..58bdac92 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/bancoBanrisul.test.ts @@ -0,0 +1,12 @@ +import { BankValidator } from "./bankValidator"; + +export class BancoBanrisulValidator extends BankValidator{ + + protected isValidFormat(bankAccount: string): boolean { + return /^\d{8}\-(\d{1}|x)$/.test(bankAccount); + } + + protected checkDV(bankAccount: string): boolean { + return true; + } +} diff --git a/src/utilities/conta-bancaria/__tests__/bancoDoBrasil.test.ts b/src/utilities/conta-bancaria/__tests__/bancoDoBrasil.test.ts new file mode 100644 index 00000000..df60ff87 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/bancoDoBrasil.test.ts @@ -0,0 +1,20 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("001", '00210169-6')).toBe(true); + expect(isValidAccount("001", '1171032-2')).toBe(true); + expect(isValidAccount("001", '19932-X')).toBe(true); + expect(isValidAccount("001", '1050769-8')).toBe(true); + expect(isValidAccount("001", '87889-8')).toBe(true); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("001", '')).toBe(false); + expect(isValidAccount("001", '123456789-0')).toBe(false); + expect(isValidAccount("001", 'asdfasdf')).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/__tests__/bradesco.test.ts b/src/utilities/conta-bancaria/__tests__/bradesco.test.ts new file mode 100644 index 00000000..d152b763 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/bradesco.test.ts @@ -0,0 +1,27 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("237", '0238069-2')).toBe(true); + expect(isValidAccount("237", '0301357-P')).toBe(true); + expect(isValidAccount("237", '0325620-0')).toBe(true); + expect(isValidAccount("237", '0284025-1')).toBe(true); + expect(isValidAccount("237", '0195470-9')).toBe(true); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("237", '')).toBe(false); + expect(isValidAccount("237", '123456789-0')).toBe(false); + expect(isValidAccount("237", 'asdfasdf')).toBe(false); + }) + }) + describe("invalid account",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("237", '0195470-1')).toBe(false); + expect(isValidAccount("237", '0195471-P')).toBe(false); + expect(isValidAccount("237", '0195491-0')).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/__tests__/caixaEconomicaFederal.test.ts b/src/utilities/conta-bancaria/__tests__/caixaEconomicaFederal.test.ts new file mode 100644 index 00000000..ad5361b5 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/caixaEconomicaFederal.test.ts @@ -0,0 +1,21 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("104", '00100000448-6', "2004")).toBe(true); + }) + }) + describe("invalid accounts",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("104", '00100000448-7', "0189")).toBe(false); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("104", '', "")).toBe(false); + expect(isValidAccount("104", '123456789-0', "")).toBe(false); + expect(isValidAccount("104", 'asdfasdf', "")).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/__tests__/citibank.test.ts b/src/utilities/conta-bancaria/__tests__/citibank.test.ts new file mode 100644 index 00000000..cc123e83 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/citibank.test.ts @@ -0,0 +1,22 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("745", '0007500465-8', "0075")).toBe(true); + expect(isValidAccount("745", '1420412612-0', "0013")).toBe(true); + }) + }) + describe("invalid accounts",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("745", '1420412612-1', "0013")).toBe(false); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("745", '', "")).toBe(false); + expect(isValidAccount("745", '123456789-0', "")).toBe(false); + expect(isValidAccount("745", 'asdfasdf', "")).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/__tests__/hsbc.test.ts b/src/utilities/conta-bancaria/__tests__/hsbc.test.ts new file mode 100644 index 00000000..0b85e8ec --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/hsbc.test.ts @@ -0,0 +1,21 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("399", '853838-6', "0007")).toBe(true); + }) + }) + describe("invalid accounts",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("399", '853838-5', "0007")).toBe(false); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("399", '', "")).toBe(false); + expect(isValidAccount("399", '123456789-0', "")).toBe(false); + expect(isValidAccount("399", 'asdfasdf', "")).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/__tests__/itau.test.ts b/src/utilities/conta-bancaria/__tests__/itau.test.ts new file mode 100644 index 00000000..6aadb806 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/itau.test.ts @@ -0,0 +1,23 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("341", '02366-1', "2545")).toBe(true); + expect(isValidAccount("341", '98197-0', "0031")).toBe(true); + expect(isValidAccount("341", '11745-6', "4102")).toBe(true); + }) + }) + describe("invalid accounts",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("341", '11745-6', "0031")).toBe(false); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("341", '', "")).toBe(false); + expect(isValidAccount("341", '123456789-0', "")).toBe(false); + expect(isValidAccount("341", 'asdfasdf', "")).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/__tests__/real.test.ts b/src/utilities/conta-bancaria/__tests__/real.test.ts new file mode 100644 index 00000000..d7d07e97 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/real.test.ts @@ -0,0 +1,21 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("356", '5711460-9', "1835")).toBe(true); + }) + }) + describe("invalid accounts",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("356", '5711460-0', "1835")).toBe(false); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("356", '', "")).toBe(false); + expect(isValidAccount("356", '123456789-0', "")).toBe(false); + expect(isValidAccount("356", 'asdfasdf', "")).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/__tests__/santander.test.ts b/src/utilities/conta-bancaria/__tests__/santander.test.ts new file mode 100644 index 00000000..04614689 --- /dev/null +++ b/src/utilities/conta-bancaria/__tests__/santander.test.ts @@ -0,0 +1,21 @@ +import { isValidAccount } from '..'; + +describe("isValid", ()=>{ + describe("valid accounts",()=>{ + it("returns true", ()=>{ + expect(isValidAccount("033", '01017417-9', "0189")).toBe(true); + }) + }) + describe("invalid accounts",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("033", '01017417-1', "0189")).toBe(false); + }) + }) + describe("wrong format",()=>{ + it("returns false", ()=>{ + expect(isValidAccount("001", '', "")).toBe(false); + expect(isValidAccount("001", '123456789-0', "")).toBe(false); + expect(isValidAccount("001", 'asdfasdf', "")).toBe(false); + }) + }) +}) diff --git a/src/utilities/conta-bancaria/index.ts b/src/utilities/conta-bancaria/index.ts new file mode 100644 index 00000000..e74e5f62 --- /dev/null +++ b/src/utilities/conta-bancaria/index.ts @@ -0,0 +1,33 @@ +import { CitibankValidator } from './validators/citibank'; +import { HSBCValidator } from './validators/hsbc'; +import { RealValidator } from './validators/real'; +import { ItauValidator } from './validators/itau'; +import { BradescoValidator } from './validators/bradesco'; +import { CaixaEconomicaFederalValidator } from './validators/caixaEconomicaFederal'; +import { SantanderValidator } from './validators/santander'; +import { BancoDoBrasilValidator } from './validators/bancoDoBrasil'; + +type BankCode = "001" | "033" | "104" | "237" | "341" | "356" | "399" | "745"; + +const BANKS = { + "001": BancoDoBrasilValidator, + "033": SantanderValidator, + "104": CaixaEconomicaFederalValidator, + "237": BradescoValidator, + "341": ItauValidator, + "356": RealValidator, + "399": HSBCValidator, + "745": CitibankValidator +} + +/** + * + * @param bankCode 001: Banco do Brasil, 033: Santander, 104: CEF, 237: Bradesco, 341: Itau, 356: Real, 399: HSBC, 745: Citibank + * @param accountNumber + * @param agencyNumber Banks 003, 104, 237, 341, 356, 399 and 745 requires agencyNumber + * @returns + */ +export function isValidAccount(bankCode:BankCode, accountNumber: string, agencyNumber?: string) { + const bank = new BANKS[bankCode](); + return bank.isValid(accountNumber, agencyNumber); +} diff --git a/src/utilities/conta-bancaria/validators/bancoDoBrasil.ts b/src/utilities/conta-bancaria/validators/bancoDoBrasil.ts new file mode 100644 index 00000000..93bd5ae7 --- /dev/null +++ b/src/utilities/conta-bancaria/validators/bancoDoBrasil.ts @@ -0,0 +1,31 @@ +import { BankValidator } from "./bankValidator"; + +export class BancoDoBrasilValidator extends BankValidator{ + + protected isValidFormat(bankAccount: string): boolean { + return /^\d{1,8}(\-(\d{1}|X))?$/.test(bankAccount); + } + + protected checkDV(bankAccount: string): boolean { + const bankAccountSplited = bankAccount.split("-"); + const account = bankAccountSplited[0].length < 8 ? this.formatLeftZeros(8 - bankAccountSplited[0].length, bankAccountSplited[0]) : bankAccountSplited[0]; + const verificationDigit = bankAccountSplited[1] || 'X' + return this.getAccountVerificationDigit(account) === verificationDigit; + } + + private formatLeftZeros(count:number, initialValue:string):string{ + if(count) { + return this.formatLeftZeros(count-1, `0${initialValue}`) + } + return initialValue; + } + + private getAccountVerificationDigit(accountNumber:string){ + const digitSum = accountNumber.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (9-index),0); + const dv = (11 - digitSum%11); + if(dv === 10) return "X"; + if(dv === 11) return "0"; + return dv.toString() + } + +} diff --git a/src/utilities/conta-bancaria/validators/bankValidator.ts b/src/utilities/conta-bancaria/validators/bankValidator.ts new file mode 100644 index 00000000..3e0187c9 --- /dev/null +++ b/src/utilities/conta-bancaria/validators/bankValidator.ts @@ -0,0 +1,9 @@ +export abstract class BankValidator { + public isValid(bankAccount: string, bankAgency?: string): boolean { + return this.isValidFormat(bankAccount,bankAgency) && this.checkDV(bankAccount, bankAgency); + } + + protected abstract isValidFormat(bankAccount: string, bankAgency?: string): boolean; + + protected abstract checkDV(bankAccount: string, bankAgency?: string): boolean; +} diff --git a/src/utilities/conta-bancaria/validators/bradesco.ts b/src/utilities/conta-bancaria/validators/bradesco.ts new file mode 100644 index 00000000..db9f2ad4 --- /dev/null +++ b/src/utilities/conta-bancaria/validators/bradesco.ts @@ -0,0 +1,22 @@ +import { BankValidator } from "./bankValidator"; + +export class BradescoValidator extends BankValidator{ + protected isValidFormat(bankAccount: string): boolean { + return /^\d{7}\-(\d{1}|P)?$/.test(bankAccount); + } + + protected checkDV(bankAccount: string): boolean { + const bankAccountSplited = bankAccount.split("-"); + const account = bankAccountSplited[0]; + const verificationDigit = bankAccountSplited[1]; + return this.getAccountVerificationDigit(account) === verificationDigit; + } + + private getAccountVerificationDigit(accountNumber:string){ + const digitSum = accountNumber.split("").reverse().map(Number).reduce((acc,cur,index)=> acc + cur * ((index%7+2)),0); + const rest = digitSum%11; + if(rest === 1) return "P"; + if(rest === 0) return "0"; + return (11 - rest).toString() + } +} diff --git a/src/utilities/conta-bancaria/validators/caixaEconomicaFederal.ts b/src/utilities/conta-bancaria/validators/caixaEconomicaFederal.ts new file mode 100644 index 00000000..9aec4515 --- /dev/null +++ b/src/utilities/conta-bancaria/validators/caixaEconomicaFederal.ts @@ -0,0 +1,25 @@ +import { BankValidator } from "./bankValidator"; + +export class CaixaEconomicaFederalValidator extends BankValidator{ + protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { + return /^\d{11}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); + } + + protected checkDV(bankAccount: string, agencyNumber:string): boolean { + const account = bankAccount.split("-")[0]; + const accountType = account.slice(0,3); + const accountNumber = account.slice(-8) + const verificationDigit = bankAccount.split("-")[1]; + return this.getAccountVerificationDigit(accountNumber, agencyNumber, accountType) === verificationDigit; + } + + private getAccountVerificationDigit(accountNumber:string, agencyNumber:string, accountType: string){ + const agencySum = agencyNumber.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (8 - index),0); + const accountTypeSum = accountType.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (4 - index),0); + const accountNumberSum = accountNumber.split("").map(Number).reduce((acc,cur,index)=> acc + cur * (9 - index),0); + const sum = (agencySum + accountNumberSum + accountTypeSum)* 10 + const mod = Math.trunc(sum/11); + const rest = Math.abs(mod * 11 - sum) + return (rest === 10 ? 0: rest).toString(); + } +} diff --git a/src/utilities/conta-bancaria/validators/citibank.ts b/src/utilities/conta-bancaria/validators/citibank.ts new file mode 100644 index 00000000..09fdc668 --- /dev/null +++ b/src/utilities/conta-bancaria/validators/citibank.ts @@ -0,0 +1,20 @@ +import { BankValidator } from "./bankValidator"; + +export class CitibankValidator extends BankValidator{ + protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { + return /^\d{7}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); + } + + protected checkDV(bankAccount: string, bankAgency:string): boolean { + const accountNumber = bankAccount.split("-")[0]; + const verificationDigit = bankAccount.split("-")[1]; + return this.getAccountVerificationDigit(accountNumber, bankAgency) === verificationDigit; + } + + private getAccountVerificationDigit(accountNumber:string, agencyNumber:string){ + const sum = `${agencyNumber}${accountNumber}`.split("").map(Number).reduce((acc,cur,index) => acc + cur * (11-index),0); + const rest = (sum - Math.trunc(sum / 11) * 11); + if (rest > 1) return (11 - rest).toString() + return "0" + } +} diff --git a/src/utilities/conta-bancaria/validators/hsbc.ts b/src/utilities/conta-bancaria/validators/hsbc.ts new file mode 100644 index 00000000..ef55e236 --- /dev/null +++ b/src/utilities/conta-bancaria/validators/hsbc.ts @@ -0,0 +1,23 @@ +import { BankValidator } from "./bankValidator"; + +export class HSBCValidator extends BankValidator{ + private agencyMultiplicationWeights = [8,9,2,3]; + + protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { + return /^\d{6}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); + } + + protected checkDV(bankAccount: string, bankAgency:string): boolean { + const accountNumber = bankAccount.split("-")[0]; + const verificationDigit = bankAccount.split("-")[1]; + return this.getAccountVerificationDigit(accountNumber, bankAgency) === verificationDigit; + } + + private getAccountVerificationDigit(accountNumber:string, agencyNumber:string){ + const agencySum = agencyNumber.split("").map(Number).reduce((acc,cur,index) => acc + cur * this.agencyMultiplicationWeights[index],0); + const accountSum = accountNumber.split("").map(Number).reduce((acc,cur,index) => acc + cur * (index + 4),0); + const rest = (agencySum+accountSum)%11; + if (rest === 10) return "0" + return rest.toString(); + } +} diff --git a/src/utilities/conta-bancaria/validators/itau.ts b/src/utilities/conta-bancaria/validators/itau.ts new file mode 100644 index 00000000..759b6f24 --- /dev/null +++ b/src/utilities/conta-bancaria/validators/itau.ts @@ -0,0 +1,35 @@ +import { isEven } from "../../../helpers"; +import { BankValidator } from "./bankValidator"; + +export class ItauValidator extends BankValidator{ + protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { + return /^\d{5}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); + } + + protected checkDV(bankAccount: string, agencyNumber:string): boolean { + const account = bankAccount.split("-")[0]; + const verificationDigit = bankAccount.split("-")[1]; + return this.getAccountVerificationDigit(account, agencyNumber) === verificationDigit; + } + + private getAccountVerificationDigit(accountNumber:string, agencyNumber:string){ + const agencySum = agencyNumber.split("").map(Number).reduce((acc,cur, index)=> { + const digitMultiplication = cur * (isEven(index + 1)? 1 : 2) + return acc + (digitMultiplication > 9 ? this.sumDigits(digitMultiplication) : digitMultiplication) + } ,0); + const accountNumberSum = accountNumber.split("").map(Number).reduce((acc,cur, index)=> { + const digitMultiplication = cur * (isEven(index + 1)? 1 : 2) + return acc + (digitMultiplication > 9 ? this.sumDigits(digitMultiplication) : digitMultiplication) + } ,0); + const rest = (agencySum+accountNumberSum)%10; + return (10 - rest).toString(); + } + + private sumDigits(number:number):number{ + return number + .toString() + .split('') + .map(Number) + .reduce((a, b) => a + b, 0); + } +} diff --git a/src/utilities/conta-bancaria/validators/real.ts b/src/utilities/conta-bancaria/validators/real.ts new file mode 100644 index 00000000..f582275b --- /dev/null +++ b/src/utilities/conta-bancaria/validators/real.ts @@ -0,0 +1,25 @@ +import { BankValidator } from "./bankValidator"; + +export class RealValidator extends BankValidator{ + + private multiplicationWeights = [8,1,4,7,2,2,5,9,3,9,5]; + + protected isValidFormat(bankAccount: string, agencyNumber:string): boolean { + return /^\d{7}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(agencyNumber); + } + + protected checkDV(bankAccount: string, bankAgency:string): boolean { + const accountNumber = bankAccount.split("-")[0]; + const verificationDigit = bankAccount.split("-")[1]; + return this.getAccountVerificationDigit(accountNumber, bankAgency) === verificationDigit; + } + + private getAccountVerificationDigit(accountNumber:string, agencyNumber:string){ + const sum = `${agencyNumber}${accountNumber}`.split("").map(Number).reduce((acc,cur,index) => acc + cur * this.multiplicationWeights[index],0); + const rest = (sum - Math.trunc(sum / 11) * 11); + + if (rest === 1) return "0" + if (rest === 0) return "1" + return (11 - rest).toString(); + } +} diff --git a/src/utilities/conta-bancaria/validators/santander.ts b/src/utilities/conta-bancaria/validators/santander.ts new file mode 100644 index 00000000..55d0340d --- /dev/null +++ b/src/utilities/conta-bancaria/validators/santander.ts @@ -0,0 +1,33 @@ +import { BankValidator } from "./bankValidator"; + +export class SantanderValidator extends BankValidator{ + private accountTypes = ["01","02","03","05","07","09","13","27","35","37","43","45","46","48","50","53","60","92"]; + private agencyMultypliers = [9,7,3,1]; + private accountMultypliers = [9,7,1,3,1,9,7,3]; + + protected isValidFormat(bankAccount: string, bankAgency:string): boolean { + return this.checkAccountType(bankAccount) && /^\d{8}\-\d{1}$/.test(bankAccount) && /^\d{4}$/.test(bankAgency); + } + + protected checkDV(bankAccount: string, bankAgency:string): boolean { + const accountNumber = bankAccount.split("-")[0]; + const verificationDigit = bankAccount.split("-")[1]; + + return this.getAccountVerificationDigit(accountNumber,bankAgency) === verificationDigit; + } + + private getAccountVerificationDigit(accountNumber:string, agencyNumber:string){ + const agencySum = agencyNumber.split("").map(Number).reduce((acc,cur,index)=> acc + this.getLastDigit(cur * this.agencyMultypliers[index]),0); + const accountSum = accountNumber.split("").map(Number).reduce((acc,cur,index)=> acc + this.getLastDigit(cur * this.accountMultypliers[index]),0); + return (10 - this.getLastDigit(agencySum+accountSum)).toString() +} + + private getLastDigit(number:number){ + return parseInt(number.toString().slice(-1)) + } + + private checkAccountType(bankAccount:string): boolean { + const accountType = bankAccount.slice(0,2); + return this.accountTypes.includes(accountType); + } +}