Banxico.CLABE.psm1

$script:ModuleRoot = $PSScriptRoot

<#
.SYNOPSIS
 
Converts HTML table rows and columns into an object array.
 
.DESCRIPTION
 
Converts HTML table rows and columns from Banxico's web site into a list of
banking institutions as an object array.
 
.PARAMETER Html
Specifies the HTML string to be processed. Mandatory.
 
.OUTPUTS
 
System.Object[]. ConvertFrom-HtmlData returns an object array from the html string.
 
.EXAMPLE
 
PS> ConvertFrom-HtmlData -Html "<tr><td>99999</td><td>TEST</td></tr>"
 
Code Institution
---- -----------
999 TEST
 
#>

function ConvertFrom-HtmlData
{
    [OutputType([System.Object[]])]
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [string]
        $Html
    )

    begin
    {
        $Output = @()
    }
    process
    {
        # Matches lines like "<tr><td>40044</td><td>SCOTIABANK</td></tr>"
        $Pattern = '<tr\>\<td\>(?<Code>[\d]+)\<\/td\>\<td\>(?<Institution>[\w]+)\<\/td\>\<\/tr\>'

        $Results = $Html | Select-String $Pattern -AllMatches

        $Results.Matches | ForEach-Object {
            $Groups = $_ | Select-Object -ExpandProperty Groups
            $Output += [pscustomobject]@{
                # CLABE only uses the last 3 digits for each institution code
                Code        = $Groups.Value[1].Substring($Groups.Value[1].Length - 3)
                Institution = $Groups.Value[2]
            }
        }
    }
    end
    {
        return $Output
    }
}


<#
.SYNOPSIS
 
Tests a CLABE code by performing calculations.
 
.DESCRIPTION
 
Tests a CLABE code by performing calculations to verify the control digit.
 
.PARAMETER CLABE
Specifies the CLABE code to be validated. Mandatory.
 
.OUTPUTS
 
PSCustomObject. Test-CLABE returns an object with the result of the calculations.
 
.EXAMPLE
 
PS> Test-CLABE -CLABE 044939000118359712
 
ControlDigit Success
------------ -------
2 True
 
#>

function Test-CLABE
{
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [ValidatePattern('^[\d]{18}$')]
        [string]
        $CLABE
    )
    
    begin
    {
        $Output = [pscustomobject]@{
            ControlDigit  = $CLABE.Substring(17, 1)
            Success       = $false
        }
    }
    process
    {
        $ModulusSum    = 0
        $Positions     = @(3, 7, 1)
        $PositionIndex = 0

        foreach ($char in $CLABE.Substring(0, 17).ToCharArray())
        {
            $ModulusSum += ([int]::Parse($char) * [int]::Parse($Positions[$PositionIndex])) % 10

            $PositionIndex += 1

            if ($PositionIndex -ge 3)
            {
                $PositionIndex = 0
            }
        }

        $ControlDigit = (10 - ($ModulusSum % 10)) % 10

        $Output.Success = $Output.ControlDigit -eq $ControlDigit
    }
    end
    {
        return $Output
    }
}


<#
.SYNOPSIS
 
Lists the branch offices or cities used by CLABE.
 
.DESCRIPTION
 
Lists the branch offices or cities used by CLABE where the checking account is located
within Mexico.
 
Be aware that there might be multiple branch offices under the same code.
 
.PARAMETER Code
Specifies the branch office code to be searched for. Optional.
 
.OUTPUTS
 
System.Object[]. Get-BranchOfficeData returns an object array of branch offices.
 
.EXAMPLE
 
PS> Get-BranchOfficeData
 
Code BranchOffice
---- ------------
001 TEST1
002 TEST2
003 TEST3
...
 
.EXAMPLE
 
PS> Get-BranchOfficeData -Code 999
 
Code BranchOffice
---- ------------
999 TEST
 
#>

function Get-BranchOfficeData
{
    [CmdletBinding()]
    Param (
        [Parameter()]
        [ValidatePattern('^[\d]{3}$')]
        [string]
        $Code
    )

    begin
    {
        $Data = @()
    }
    process
    {
        $Path = $PSScriptRoot
        $Leaf = Split-Path -Leaf -Path $Path

        if ($Leaf -eq 'functions')
        {
            $Path = Split-Path -Parent -Path $Path
        }

        $Data = Import-Csv -Path "$Path\internal\data\BranchOffices.csv"
    }
    end
    {
        if ($Code)
        {
            $Data = $Data | Where-Object {$_.Code -eq $Code}
        }

        return $Data
    }
}


<#
.SYNOPSIS
 
Displays the details about a CLABE.
 
.DESCRIPTION
 
Displays the details about a CLABE like its banking institution and branch office, also
validates the control digit.
 
Validation could be subject to change or failure as it depends on external catalogs.
 
.PARAMETER CLABE
Specifies the CLABE code to be inspected. Mandatory.
 
.OUTPUTS
 
PSCustomObject. Get-CLABEInfo returns an object with details about a CLABE.
 
.EXAMPLE
 
PS> Get-CLABEInfo -CLABE 044939000118359712
 
Institution : TEST (044)
BranchOffice : TEST (939)
AccountNumber : 00011835971
ControlDigit : 2
Valid : True
 
#>

function Get-CLABEInfo
{
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [ValidatePattern('^[\d]{18}$')]
        [string]
        $CLABE
    )

    begin
    {
        $Output = [pscustomobject]@{
            Institution   = ''
            BranchOffice  = ''
            AccountNumber = ''
            ControlDigit  = ''
            Valid         = $false
        }

        $ValidData        = $true
        $InstitutionCode  = $CLABE.Substring(0, 3)
        $BranchOfficeCode = $CLABE.Substring(3, 3)
        $ControlDigit     = $CLABE.Substring(17, 1)
    }
    process
    {
        # Institution
        $InstitutionData = Get-InstitutionData -Code $InstitutionCode

        if (-not $InstitutionData)
        {
            $ValidData   = $false
            $Institution = "UNKNOWN ($InstitutionCode)"
            Write-Warning "Unknown Institution with code: $InstitutionCode"
        }
        else
        {
            $Institution = "$($InstitutionData.Institution) ($($InstitutionData.Code))"
        }

        # Branch Office
        $BranchOfficeData = Get-BranchOfficeData -Code $BranchOfficeCode

        if (-not $BranchOfficeData)
        {
            $ValidData   = $false
            $BranchOffice = "UNKNOWN ($BranchOfficeCode)"
            Write-Warning "Unknown Branch Office with code: $BranchOfficeCode"
        }
        else
        {
            $BranchOffice = "$($BranchOfficeData.BranchOffice -join ', ') ($($BranchOfficeData.Code | Select-Object -Unique))"
        }

        # Control Digit
        $ControlDigitTest = Test-CLABE -CLABE $CLABE

        if ($ControlDigitTest.Success -eq $false)
        {
            $ValidData = $false
            Write-Warning "Invalid Control Digit ($ControlDigit)"
        }

        $Output.Institution   = $Institution
        $Output.BranchOffice  = $BranchOffice
        $Output.AccountNumber = $CLABE.Substring(6, 11)
        $Output.ControlDigit  = $ControlDigit
        $Output.Valid         = $ValidData
    }
    end
    {
        return $Output
    }
}


<#
.SYNOPSIS
 
Retrieves a list of banking institutions from Banxico's web site.
 
.DESCRIPTION
 
Retrieves a list of banking institutions from Banxico's web site as html to be processed
internally in order to generate an object array of banking institutions.
 
.PARAMETER Code
Specifies the institution code to be searched for. Optional.
 
.OUTPUTS
 
System.Object[]. Get-InstitutionData returns an object array of banking institutions.
 
.EXAMPLE
 
PS> Get-InstitutionData
 
Code Institution
---- -----------
001 TEST1
002 TEST2
003 TEST3
...
 
.EXAMPLE
 
PS> Get-InstitutionData -Code 999
 
Code Institution
---- -----------
999 TEST
 
#>

function Get-InstitutionData
{
    [CmdletBinding()]
    Param (
        [Parameter()]
        [ValidatePattern('^[\d]{3}$')]
        [string]
        $Code
    )
    
    begin
    {
        $Data = @()
    }
    process
    {
        $Response = Invoke-WebRequest -Uri 'https://www.banxico.org.mx/cep-scl/listaInstituciones.do'

        if ($Response.StatusCode -ne 200)
        {
            Write-Error "Unable to retrieve data from Banxico."
        }
        else
        {
            $Data = ConvertFrom-HtmlData -Html $Response.Content
        }
    }
    end
    {
        if ($Code)
        {
            $Data = $Data | Where-Object {$_.Code -eq $Code}
        }

        return $Data
    }
}


Export-ModuleMember -Function 'Get-BranchOfficeData','Get-CLABEInfo','Get-InstitutionData'