An application written in PHP (Symfony 8 framework) to import, categorize and analyze transactions.
- Being able to categorize transactions from a monthly account statement in seconds, not minutes (obviously initial setup will take longer).
- Build every chart one can think of to make sense of expenses and revenues.
Follow the workflow to get to know how to use the app.
docker compose up -d mysql
docker compose logs -f mysql # wait for mysql to be ready
docker compose up -d # doctrine migrations will run as soon as the php container get started
docker compose run php composer install
# allow the web user from the php container to write in the /var/statements folder
docker exec -it expenses-categorizer-account_statement_parser-1 chown 1000 -R /var/statements
# optionally, create some default transaction categories
docker compose run php php bin/console doctrine:fixtures:load --append
# or import a dump
docker compose exec -T mysql mysql -uexpenses_categorizer -pexpenses_categorizer expenses_categorizer < dump.sqlBrowse http://localhost
XDEBUG=TRUE docker compose up -d --builddocker compose run php php vendor/bin/phpunitThe current implementation can import transactions from account statements coming from the following banks:
- Boursorama: pdf statement
- Caisse d'épargne: pdf statement
- Crédit Mutuel: pdf statement
- Fortuneo: pdf statement
- N26: pdf statement
- National Bank of Canada (Banque National du Canada): csv export
Source code for these parsers can be found at https://github.com/Ovski4/account-statement-parsers.
To add a new file parser, create a new class that implements AbstractFileParser. The symfony framework will take care of creating tagged services automatically and update the user interface (the forms) accordingly.
You will have to implement the following methods:
- parse() : returns an array of Transactions objects.
- getName() : returns a string that will appear in urls
- getLabel() : returns a string that will be used in forms and alerts (a human readable string).
Here is an example below:
<?php
namespace App\Services\FileParser;
use App\Entity\Transaction;
class HelloBankAccountStatementParser extends AbstractFileParser
{
private $accountRepository;
public function __construct(AccountRepository $accountRepository)
{
$this->accountRepository = $accountRepository;
}
public function getName(): string
{
return 'hello-bank';
}
public function getLabel(): string
{
return 'Hello Bank account statement';
}
public function parse(string $filepath, array $options = []): array
{
$results = ... // do your magic here with the file
$transactions = [];
foreach($results as $result) {
$transaction = new Transaction();
$transaction
->setAmount($result['value'])
->setCreatedAt(
(\DateTime::createFromFormat('d/m/Y', $result['date']))->setTime(0, 0, 0)
)
->setLabel($result['label'])
->setAccount($this->accountRepository->findByAliasOrName($result['account']))
;
$transactions[] = $transaction;
}
return $transactions;
}
}Code style follows the @Symfony standard, enforced by php-cs-fixer. PHPStan runs
at level 5, with the pre-existing errors captured in phpstan-baseline.neon.
Run every check:
docker compose run --rm --no-deps -e RUN_MIGRATIONS=false php composer lint
Rewrite files to match the coding standard:
docker compose run --rm --no-deps -e RUN_MIGRATIONS=false php composer fix:style
Individual checks are available as composer lint:style, lint:stan, lint:yaml,
lint:twig and lint:container. The same checks run on every push through the
run-linters workflow. VS Code users get these as tasks (Tasks: Run Task).