RVR.Declarations.psm1

function Send-Declaration {
    <#
       .SYNOPSIS
        Send the declaration using Outlook.
 
       .DESCRIPTION
       Send the declaration using Outlook.
     
       .PARAMETER recipient
       The recipient to send the declaration to
 
       .PARAMETER subject
       The email message subject.
 
       .PARAMETER Transactions
       The transactions to include in the mail message
 
       .PARAMETER SumAmount
       The total amount of declarations (eur)
 
       .PARAMETER SMTPSendCredential
       The credentials used to authenticate with the SMTP server smtp.office365.com.
       You're required to authenticate using your O365 credentials.
        
       An [System.Management.Automation.PSCredential] object is expected.
     
    #>


    [cmdletbinding()]
    param(
        $Recipient = 'robertreems@gmail.com',
        $Subject = 'Parkeerdeclaraties Robert van Reems',
        $Transactions,
        $SumAmount,

        # The credential used for the SMTP connection
        [parameter(Mandatory = $true)]
        [System.Management.Automation.PSCredential] $SMTPSendCredential
    )

    begin {

        # Compile the Message body
        $Body = "Beste collega,
 
        Hierbij de declaraties van mijn parkeerkosten in Den Bosch Pettelaar park. Deze kosten zijn gemaakt voor mijn werkzaamheden bij de Volksbank.
 
        De transacties staan hieronder:
        $Transactions
 
        Het totaal bedrag komt neer op $SumAmount Euro
 
        Alvast bedankt voor het verwerken."



    }

    process {

        # The hashtable with splatted arguments for easy reading.
        $HashArguments = @{
            To = $Recipient
            Body = $Body
            SmtpServer = 'smtp.office365.com' 
            From =  $SMTPSendCredential.UserName
            Subject = $Subject
            Credential = $SMTPSendCredential
            UseSsl = $true
        }

        # Send the message
        Write-Verbose "Sending email message to $Recipient"
        Send-MailMessage @HashArguments -ErrorAction Stop
    }

    end {
            Write-Verbose 'Ending normally'
    }
}

function Get-BankTransaction {

    <#
       .SYNOPSIS
        The function that imports the CSV exported from the bank
 
       .DESCRIPTION
       The function that imports the CSV exported from the bank
     
       .PARAMETER Path
       Path to the CSV-file
     
    #>


    [cmdletbinding()]
    param (
        [ValidateScript( {
                if ( -Not ($_ | Test-Path) ) {
                    throw "File or folder does not exist"
                    exit
                }
                return $true
            })]
        [System.IO.FileInfo]$Path
    )
    
    # The predefined header for the CSV-file
    # TODO update columns
    $header = 'Date', 'Account', 'CounterAccount', 'ReadableOrgName', 'Kolom4', 'Kolom5', 'Kolom6', 'Currency', 'Kolom8', 'Currency2', 'Amount', 'Date2', 'Date3', 'Unknown', 'Unknown1', 'Unknown2', 'Unknown3', 'Description', 'Unknown4'
        
    $Transactions = [System.Collections.ArrayList]@()
    
    Write-Verbose "Importing $path"
    try {
        $Transactions += Import-Csv -Delimiter ';' -Path $Path -Header $header    
    }
    catch {
        throw "Failed to import CSV-file $Path with error: $_"
        exit
    }

    Write-Verbose "Imported transactions is: $($Transactions.count)"

    Write-Verbose 'Filtering the transactions'
    $FilteredTransactions = $Transactions.Where( { $_.Description -like '*PETTELR*' -and $_.Amount -eq '-4.00' })    
    
    return $FilteredTransactions
}

function Test-TransactionIsDeclared {
    <#
       .SYNOPSIS
       Tests if the transaction is allready declared or not.
     
       .DESCRIPTION
       Tests if the transaction is allready declared or not.
     
       .PARAMETER Transaction
       The transaction to test
     
       .PARAMETER Path
       Path to the database file (CSV) containing allready declared transactions
     
    #>


    [cmdletbinding()]
    [OutputType([boolean])] 

    param (
        [parameter()]
        [ValidateNotNull()]
        $Transaction, 
        
        [parameter()]
        [ValidateScript( {
                if ( -Not ($_ | Test-Path) ) {
                    throw "File or folder does not exist"
                    exit
                }
                return $true
            })]
        [System.IO.FileInfo]$Path
    )
    
    begin {
        
        # Import the CSV to $Declared CSV
        # I'm using script scope so the contents can be cached.

        # Check if cache exists
        if (-not $Script:DeclaredTransactions) {

            try {
                Write-Verbose "Importing Declared transactions file $path"
                $Script:DeclaredTransactions = Import-Csv $Path 
                
            }
            catch {
                throw "Failed to import declared transactions file $path with error $_ " 
                exit
            }
        }
    }

    process {
        # Check if there are declared transactions
        if ($Script:DeclaredTransactions.count -gt 1) {

            # If there are declared transactions check them.
            if ( $Script:DeclaredTransactions.Description.Contains($transaction.Description) ) {
                return $true
            }
            else {
                return $false
            }
        }
    }

    end {

    }
}

function Get-ToDeclareTransaction {

    <#
       .SYNOPSIS
       This returnes the transactions that have yet to be declared.
     
       .DESCRIPTION
       This returnes the transactions that have yet to be declared.
     
       .PARAMETER BankTransactionsPath
       The path to the Bank transactions CSV file
 
       .PARAMETER DeclaredTransactionsPath
        Path to the database file (CSV) containing allready declared transactions
     
    #>

    
    [cmdletbinding()]
    param(
        [ValidateScript( {
                if ( -Not ($_ | Test-Path) ) {
                    throw "File or folder does not exist"
                    exit
                }
                return $true
            })]
        [System.IO.FileInfo]$BankTransactionsPath,

        $DeclaredTransactionsPath
    )

    begin {
        # Get the Bank transactions from CSV
        Write-Verbose "Importing bank transactions $BankTransactionsPath"
        $BankTransactions = Get-BankTransaction -ErrorAction Stop -Path $BankTransactionsPath
    }

    process {
        # Loop through each transaction and check if it's allready declared
        $ToDeclareTransactions = @()
        foreach ($transaction in $BankTransactions) {
            if (Test-TransactionIsDeclared -Path $DeclaredTransactionsPath -Transaction $transaction ) {
                # The transaction is allready declared
                Write-Verbose "$($transaction.description) is allready declared."
            }
            else {
                Write-Verbose "$($transaction.description) is not yet declared."
                $ToDeclareTransactions += $transaction
            }
        }
    }

    end {
        $ToDeclareTransactions
    }
}

function New-Declaration {

    <#
       .SYNOPSIS
        Creates a new declaration email containing all the not yet declared transactions.
     
       .DESCRIPTION
        This function takes a CSV file from the SNS-bank to search for new transactions that have to be declared. If
        new declarations are found it sends them using Outlook.
     
       .PARAMETER BankTransactionsPath
       The path to the CSV-file exported from the bank.
 
       .PARAMETER DeclaredTransactionsPath
       Path to the database file (CSV) containing allready declared transactions.
       You should allways use the same file.
 
       .PARAMETER Recipient
       The recipient of the e-mail message.
 
       .PARAMETER SMTPSendCredential
       The credentials used to authenticate with the SMTP server smtp.office365.com.
       You're required to authenticate using your O365 credentials.
        
       An [System.Management.Automation.PSCredential] object is expected.
     
       .EXAMPLE
       Use .\test-transactions.csv as input and send the declaration.
        
       PS> New-Declaration -BankTransactionsPath .\test-transactions.csv -recipient xxx@xmail.com
     
    #>

    
    [cmdletbinding(SupportsShouldProcess=$True)]

    param(
        [parameter(Mandatory)]
        [ValidateScript( {
                if ( -Not ($_ | Test-Path) ) {
                    throw "File or folder does not exist"
                    exit
                }
                return $true
            })]
        [System.IO.FileInfo]$BankTransactionsPath,
        
        [System.IO.FileInfo]$DeclaredTransactionsPath,
        $Recipient = 'robertreems@gmail.com', 

        [parameter(Mandatory = $True)]
        [System.Management.Automation.PSCredential] $SMTPSendCredential
        
    )

    Begin {
        Write-Verbose 'Starting New-Declaration'

        # Set the $DeclaredTransactionsPath if it isn't set.
        if( $null -eq $DeclaredTransactionsPath ){

            # Set the path using my documents env var.
            $DeclaredTransactionsPath = ([Environment]::GetFolderPath("MyDocuments") )

            # Ubuntu fix: DeclaredTransactionsPath is pointing to /home/<username>.
            if($DeclaredTransactionsPath -notlike "*Document*") {
                $DeclaredTransactionsPath = Join-Path -Path $DeclaredTransactionsPath -ChildPath 'Documents'
            }

            #Add the fileName to the $DeclaredTransactionsPath
            $DeclaredTransactionsPath = Join-Path -Path $DeclaredTransactionsPath -ChildPath 'MyDeclarations.csv'

            Write-Verbose "No DeclaredTransactionsPath declared it is set to: $DeclaredTransactionsPath"
        }
        

        # Create new DeclaredTransactions CSV if it doesn't exist
        if (-not (Test-Path $DeclaredTransactionsPath) ) {
            Write-Verbose 'Create new Declared transactions file'
            
            # Create the file forcefully
            New-Item $DeclaredTransactionsPath -Force | Out-Null
        }
    }

    Process {
        # Get the transactions that should be declared
        $ToDeclareTransactions = @()
        $ToDeclareTransactions += Get-ToDeclareTransaction -BankTransactionsPath $BankTransactionsPath -DeclaredTransactionsPath $DeclaredTransactionsPath

        $sum = 0
        foreach ($transaction in $ToDeclareTransactions) {
            $sum = $sum + [int]$transaction.Amount
        }

        # Since $sum is a negative interger I'll have to convert it to positive
        $sum = -$sum 

        # utilise whatif
        If ($PSCmdlet.ShouldProcess("Sending message")) { 

            # If new transactions are found send the mail and update the $DeclaredTransactionsPath.
            if($ToDeclareTransactions.count -gt 0){
                Write-Verbose "Sum of transactions (euro): $sum"

                Send-Declaration -Transactions ($ToDeclareTransactions | Format-Table date, Amount, Description | Out-String) -SumAmount $sum -recipient $recipient -SMTPSendCredential $SMTPSendCredential

                # TODO update $DeclaredTransactions
                try {
                    Write-Verbose "Writing new transactions to $DeclaredTransactionsPath"


                    $ToDeclareTransactions | Export-Csv -Path $DeclaredTransactionsPath -Append -ErrorAction Stop -Force
                } 
                catch {
                    throw "Failed to update $DeclaredTransactionsPath with error: $_ "
                    Exit 
                }
            }
        }
    }

    End {
        # empty cache
        $Script:DeclaredTransactions = $null

        Write-Verbose 'Ending normally'
    }
}