├── .idea
├── .name
├── php.xml
├── encodings.xml
├── vcs.xml
├── modules.xml
├── misc.xml
└── ing-card.iml
├── .gitignore
├── composer.phar
├── convert.php
├── composer.json
├── LICENSE
├── README.md
├── composer.lock
└── Commands
└── ConvertCommand.php
/.idea/.name:
--------------------------------------------------------------------------------
1 | ing-card
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/workspace.xml
2 | vendor
3 | *.940
4 | *.TXT
5 |
--------------------------------------------------------------------------------
/composer.phar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/roydejong/ing-card-to-mt940/HEAD/composer.phar
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/convert.php:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | add(new ConvertCommand());
11 | $application->run();
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "softwarepunt/ingmt940",
3 | "description": "Converts CSV data to MoneyBird-compatible MT940 format",
4 | "minimum-stability": "stable",
5 | "license": "proprietary",
6 | "authors": [
7 | {
8 | "name": "Roy de Jong",
9 | "email": "roy@softwarepunt.nl"
10 | }
11 | ],
12 | "require": {
13 | "symfony/console": "^3.0"
14 | },
15 | "autoload": {
16 | "psr-4": { "SoftwarePunt\\IngCard\\": "" }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Roy de Jong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.idea/ing-card.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ingbanknetservice CSV -> ING MT940
2 | ===
3 |
4 | What?
5 | ---
6 |
7 | **Convert a CSV file, exported from ING Banknet Service (ingbanknetservice.nl), to MT940 format.**
8 |
9 | It currently converts the following information into the MT940 file:
10 |
11 | - Transaction dates
12 | - Transaction booking dates
13 | - Transaction amounts
14 | - Credit card transaction references
15 | - Recipient (names only)
16 |
17 | Why?
18 | ---
19 | We needed MT940 format to import into our bookkeeping software (MoneyBird) to process bank mutations. ING does not currently support this for their business cards.
20 |
21 | How?
22 | ---
23 | If you have an ING Business Card, this utility can work for you.
24 |
25 | **To export the CSV:**
26 |
27 | 1. If you haven't done so already, create an account on ingbanknetservice.nl
28 | 2. Sign in and select your card. Go to the "Transactions" tab.
29 | 3. Select a period, pick the comma-seperated (*Door komma's gescheiden tekst*) file format, and click "Download"
30 |
31 | **To use this utility:**
32 |
33 | 1. Download or clone the repository contents
34 | 2. Run ``composer install`` from the terminal to install dependencies
35 | 3. Execute the command: `php convert.php convert [] []`
36 |
37 | **Parameters:**
38 | All parameters are optional.
39 |
40 | - ``filename``: A custom filename. Defaults to `Transacties.TXT`. Relative to the current working directory.
41 | - ``iban``: A custom IBAN that will be used to identify your credit card, if useful to you. Defaults to `NL24INGB0001111111`.
42 |
43 | Notes
44 | ---
45 |
46 | - You'll need [composer](https://getcomposer.org/download/) and [php-cli](https://www.google.nl/search?q=install+php+cli) installed to use this tool.
47 | - The exported MT940 is not perfect. Some fields are not correctly formatted (such as the counterparty data), and e.g. the final SUM is missing. But it works for our purposes.
48 | - Both a starting and final balance of €0 is reported.
49 | - [MT940 format specifications](https://www.ing.nl/media/ING_ming_mt940s_24_juli_tcm162-46356.pdf)
50 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "hash": "692b9b72b33a60811b763b13216c5922",
8 | "content-hash": "e9323d69b66f089db4152ae033aaa971",
9 | "packages": [
10 | {
11 | "name": "symfony/console",
12 | "version": "v3.0.1",
13 | "source": {
14 | "type": "git",
15 | "url": "https://github.com/symfony/console.git",
16 | "reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3"
17 | },
18 | "dist": {
19 | "type": "zip",
20 | "url": "https://api.github.com/repos/symfony/console/zipball/ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
21 | "reference": "ebcdc507829df915f4ca23067bd59ee4ef61f6c3",
22 | "shasum": ""
23 | },
24 | "require": {
25 | "php": ">=5.5.9",
26 | "symfony/polyfill-mbstring": "~1.0"
27 | },
28 | "require-dev": {
29 | "psr/log": "~1.0",
30 | "symfony/event-dispatcher": "~2.8|~3.0",
31 | "symfony/process": "~2.8|~3.0"
32 | },
33 | "suggest": {
34 | "psr/log": "For using the console logger",
35 | "symfony/event-dispatcher": "",
36 | "symfony/process": ""
37 | },
38 | "type": "library",
39 | "extra": {
40 | "branch-alias": {
41 | "dev-master": "3.0-dev"
42 | }
43 | },
44 | "autoload": {
45 | "psr-4": {
46 | "Symfony\\Component\\Console\\": ""
47 | },
48 | "exclude-from-classmap": [
49 | "/Tests/"
50 | ]
51 | },
52 | "notification-url": "https://packagist.org/downloads/",
53 | "license": [
54 | "MIT"
55 | ],
56 | "authors": [
57 | {
58 | "name": "Fabien Potencier",
59 | "email": "fabien@symfony.com"
60 | },
61 | {
62 | "name": "Symfony Community",
63 | "homepage": "https://symfony.com/contributors"
64 | }
65 | ],
66 | "description": "Symfony Console Component",
67 | "homepage": "https://symfony.com",
68 | "time": "2015-12-22 10:39:06"
69 | },
70 | {
71 | "name": "symfony/polyfill-mbstring",
72 | "version": "v1.0.1",
73 | "source": {
74 | "type": "git",
75 | "url": "https://github.com/symfony/polyfill-mbstring.git",
76 | "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25"
77 | },
78 | "dist": {
79 | "type": "zip",
80 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/49ff736bd5d41f45240cec77b44967d76e0c3d25",
81 | "reference": "49ff736bd5d41f45240cec77b44967d76e0c3d25",
82 | "shasum": ""
83 | },
84 | "require": {
85 | "php": ">=5.3.3"
86 | },
87 | "suggest": {
88 | "ext-mbstring": "For best performance"
89 | },
90 | "type": "library",
91 | "extra": {
92 | "branch-alias": {
93 | "dev-master": "1.0-dev"
94 | }
95 | },
96 | "autoload": {
97 | "psr-4": {
98 | "Symfony\\Polyfill\\Mbstring\\": ""
99 | },
100 | "files": [
101 | "bootstrap.php"
102 | ]
103 | },
104 | "notification-url": "https://packagist.org/downloads/",
105 | "license": [
106 | "MIT"
107 | ],
108 | "authors": [
109 | {
110 | "name": "Nicolas Grekas",
111 | "email": "p@tchwork.com"
112 | },
113 | {
114 | "name": "Symfony Community",
115 | "homepage": "https://symfony.com/contributors"
116 | }
117 | ],
118 | "description": "Symfony polyfill for the Mbstring extension",
119 | "homepage": "https://symfony.com",
120 | "keywords": [
121 | "compatibility",
122 | "mbstring",
123 | "polyfill",
124 | "portable",
125 | "shim"
126 | ],
127 | "time": "2015-11-20 09:19:13"
128 | }
129 | ],
130 | "packages-dev": [],
131 | "aliases": [],
132 | "minimum-stability": "stable",
133 | "stability-flags": [],
134 | "prefer-stable": false,
135 | "prefer-lowest": false,
136 | "platform": [],
137 | "platform-dev": []
138 | }
139 |
--------------------------------------------------------------------------------
/Commands/ConvertCommand.php:
--------------------------------------------------------------------------------
1 | setName('convert')
16 | ->setDescription('Convert a ingbanknetservice CSV file to a MT940 file')
17 | ->addArgument(
18 | 'filename',
19 | InputArgument::OPTIONAL,
20 | 'The file to read and covert, relative to the current working directory'
21 | )
22 | ->addArgument(
23 | 'iban',
24 | InputArgument::OPTIONAL,
25 | 'A custom account number to use (IBAN), defaults to NL24INGB0001111111 otherwise'
26 | );
27 | }
28 |
29 | const C_DATE_BOOKED = 0;
30 | const C_DATE_OCCURED = 1;
31 | const C_SUPPLIER_NAME = 2;
32 | const C_SUPPLIER_PLACE = 3;
33 | const C_SUPPLIER_STATE = 4;
34 | const C_SUPPLIER_POSTCODE = 5;
35 | const C_MCC_SIC = 6;
36 | const C_MCC_DESCR = 7;
37 | const C_AMOUNT_ORIGINAL_EUROS = 8;
38 | const C_AMOUNT_ORIGINAL_CENTS = 9;
39 | const C_CURRENCY = 10;
40 | const C_EXCHANGE_RATE_NUMBERS = 11;
41 | const C_EXCHANGE_RATE_DECIMALS = 12;
42 | const C_AMOUNT_INVOICED_EUROS = 13;
43 | const C_AMOUNT_INVOICED_CENTS = 14;
44 | const C_MEMO = 15;
45 | const C_DEBIT_CREDIT = 16;
46 | const C_REFERENCE = 17;
47 | const C_DATE_OVERVIEW = 18;
48 | const C_NAME_ON_CARD = 19;
49 | const C_CARD_NUMBER = 20;
50 |
51 | protected function execute(InputInterface $input, OutputInterface $output)
52 | {
53 | // Determine path
54 | $filename = $input->getArgument('filename');
55 |
56 | if (empty($filename))
57 | {
58 | $filename = 'Transacties.TXT';
59 | }
60 |
61 | // Read file
62 | $output->writeln('Parsing file: ' . $filename . '...');
63 |
64 | if (!file_exists($filename))
65 | {
66 | throw new \Exception('File not found: ' . $filename);
67 | }
68 |
69 | $parsed = array_map('str_getcsv', file($filename));
70 | array_shift($parsed); // remove header
71 |
72 | if (!is_array($parsed) || count($parsed) <= 1)
73 | {
74 | throw new \Exception('Not a valid CSV file, or file does not contain any records: ' . $filename);
75 | }
76 |
77 | $output->writeln('Discovered ' . count($parsed). ' transactions in file.');
78 |
79 | // Determine file period
80 | $dateFromTs = PHP_INT_MAX;
81 | $dateToTs = -PHP_INT_MAX;
82 |
83 | foreach ($parsed as $transaction)
84 | {
85 | $trxDate = $transaction[self::C_DATE_OCCURED];
86 | $trxTs = strtotime($trxDate);
87 |
88 | if ($trxTs < $dateFromTs)
89 | {
90 | $dateFromTs = $trxTs;
91 | }
92 |
93 | if ($trxTs > $dateToTs)
94 | {
95 | $dateToTs = $trxTs;
96 | }
97 | }
98 |
99 | $output->writeln(sprintf('Transaction period reflected in file: %s - %s.', date('d-m-Y', $dateFromTs), date('d-m-Y', $dateToTs)));
100 |
101 | $output->writeln('');
102 | $output->writeln('Starting conversion to MT940 file format (ING version)...');
103 |
104 | // Begin: ING-specific headers
105 | $export = '';
106 | $export .= '{1:F01INGBNL2ABXXX0000000000}' . PHP_EOL;
107 | $export .= '{2:I940INGBNL2AXXXN}' . PHP_EOL;
108 | $export .= '{4:' . PHP_EOL;
109 |
110 | // START MT940 MESSAGE
111 | $iban = $input->getArgument('iban');
112 |
113 | if (empty($iban))
114 | {
115 | $iban = 'NL24INGB0001111111';
116 | }
117 |
118 | $export .= sprintf(':20:%s', uniqid('SPC')) . PHP_EOL; // Transaction reference, unique per file
119 | $export .= sprintf(':25:%s%s', $iban, 'EUR') . PHP_EOL; // Account number + currency code
120 | $export .= sprintf(':28C:%s', '00000') . PHP_EOL; // Document number, unused even by ING
121 | $export .= sprintf(':60F:%s%s%s%s', 'C', date('ymd', $dateFromTs), 'EUR', '0,00') . PHP_EOL; // Starting balance Credit-Date-Currency-Amount
122 |
123 | $debitTrxCount = 0;
124 | $debitAmountTotal = 0;
125 |
126 | foreach ($parsed as $trx)
127 | {
128 | $debitTrxCount++;
129 |
130 | $transactionTs = strtotime($trx[self::C_DATE_OCCURED]);
131 | $bookedTs = strtotime($trx[self::C_DATE_BOOKED]);
132 | $trxAmount = $trx[self::C_AMOUNT_INVOICED_EUROS] . ',' . $trx[self::C_AMOUNT_INVOICED_CENTS];
133 | $recipient = $trx[self::C_SUPPLIER_NAME];
134 |
135 | $debitAmountTotal += floatval($trxAmount);
136 |
137 | $output->writeln(sprintf(' -> Payment to %s on %s: €%s', $recipient, date('m-d-Y', $transactionTs), $trxAmount));
138 |
139 | // 1. Transaction line
140 | $export .= ':61:'; // Transaction start tag
141 | $export .= date('ymd', $transactionTs); // Transaction date YYMMDD
142 | $export .= date('md', $bookedTs); // Booked date MMDD
143 | $export .= 'D'; // Credit/debit
144 | $export .= $trxAmount; // Transaction amount
145 | $export .= 'N' . 'TRF'; // Swift code (N) for TRANSFER (TRF)
146 | $export .= 'EREF'; // Payment reference (betalingskenmerk)
147 | $export .= '//' . substr($trx[self::C_REFERENCE], 0, 16); // ING TRX Ref
148 | $export .= PHP_EOL;
149 | $export .= '/TRCD/00100/'; // ING Transaction Code (00100=SEPA Credit Transfer)
150 | $export .= PHP_EOL;
151 |
152 | // 2. Transaction details
153 | $export .= ':86:';
154 | $export .= sprintf('/EREF/%s/', $trx[self::C_REFERENCE]); // Payment ref
155 | $export .= sprintf('/CNTP/%s/', $trx[self::C_SUPPLIER_NAME]); // Counterparty
156 | $export .= PHP_EOL;
157 | }
158 |
159 | $export .= sprintf(':62F:%s%s%s%s', 'C', date('ymd', $dateToTs), 'EUR', '0,00') . PHP_EOL; // Final balance Credit-Date-Currency-Amount
160 | $export .= sprintf(':64:%s%s%s%s', 'C', date('ymd', $dateToTs), 'EUR', '0,00') . PHP_EOL; // Actual Final balance Credit-Date-Currency-Amount
161 | $export .= sprintf(':65:%s%s%s%s', 'C', date('ymd', $dateToTs), 'EUR', '0,00') . PHP_EOL; // Future balance Credit-Date-Currency-Amount
162 |
163 | // END: Message closure
164 | $export .= '-}';
165 |
166 | $outFile = sprintf('export_ing_card_%s.940', date('ymd', $dateFromTs));
167 | file_put_contents($outFile, $export);
168 |
169 | $actualFile = getcwd() . '/' . $outFile;
170 |
171 | $output->writeln('');
172 | $output->writeln("Wrote export file: {$actualFile}");
173 | }
174 | }
--------------------------------------------------------------------------------