CFONB 240 transfers
CFONB 240 files hold batches of transfers: one record of 240 characters per line, its first two characters being the record code.
- 31header
- 34transaction, repeated
- 39total
| Record | Parsed into | Reached with |
|---|---|---|
31 |
Header |
$transfer->getHeader() |
34 |
Transaction |
$transfer->getTransactions() |
39 |
Total |
$transfer->getTotal() |
Reading a file
Section titled “Reading a file”Cfonb240Reader::parse() takes the contents of the file and returns a list of
Transfer:
use Silarhi\Cfonb\Banking\Transfer;use Silarhi\Cfonb\Cfonb240Reader;
$reader = new Cfonb240Reader();
foreach ($reader->parse(file_get_contents('/path/to/cfonb240-file.txt')) as $transfer) { assert($transfer instanceof Transfer);}With this file:
3100000120181220E 300661077100020030401 3006610771000200304013400000220211220E0066149890000122140469732OFFICE NATL DU AAAAAAAA 300661077100020030401ZZZZZZZZ 211220 0000001712003400000320211220E0066149890000122140469732OFFICE NATL DU AAAAAAAA 300661077100020030401ZZZZZZZZ 211220 0000002872803900000420211220E 300661077100020030401 300661077100020030401 0000004652703100000120211220E 300661077100020030401 3006610771000200304013400000220221220E0066300030302100020116383CHIC WWW WWWWWWW 300661077100020030401ZZZZ ZZZZ F2012017 221220 0000000117603900000320221220E 300661077100020030401 300661077100020030401 000000063330<?php
use Silarhi\Cfonb\Cfonb240Reader;
$reader = new Cfonb240Reader();
foreach ($reader->parse(file_get_contents('transfers.txt')) as $transfer) { $header = $transfer->getHeader(); echo sprintf( "Transfer %s, account %s %s %s\n", $header->getPrevTransactionFileDate()->format('Y-m-d'), $header->getRecipientBankCode1(), $header->getRecipientCounterCode1(), $header->getRecipientAccountNumber1() );
foreach ($transfer->getTransactions() as $transaction) { echo sprintf( " #%d %s %-24s %8.2f %s\n", $transaction->getSequenceNumber(), $transaction->getSettlementDate()->format('Y-m-d'), $transaction->getRecipientName1(), $transaction->getTransactionAmount(), $transaction->getRecipientName2() ); }
$total = $transfer->getTotal(); echo sprintf(" Total #%d: %.2f\n", $total->getSequenceNumber(), $total->getTotalAmount());}Transfer 2020-12-18, account 30066 10771 00020030401 #2 2020-12-21 OFFICE NATL DU AAAAAAAA 1712.00 ZZZZZZZZ #3 2020-12-21 OFFICE NATL DU AAAAAAAA 2872.80 ZZZZZZZZ Total #4: 4652.70Transfer 2020-12-21, account 30066 10771 00020030401 #2 2020-12-22 CHIC WWW WWWWWWW 117.60 ZZZZ ZZZZ Total #3: 633.30The same, with more fields:
foreach ($reader->parse($content) as $transfer) { // Transfer header $header = $transfer->getHeader(); echo "Transfer Date: " . $header->getPrevTransactionFileDate()->format('Y-m-d') . "\n"; echo "Bank Code: " . $header->getRecipientBankCode1() . "\n"; echo "Account: " . $header->getRecipientAccountNumber1() . "\n";
// Transactions of the transfer foreach ($transfer->getTransactions() as $transaction) { echo "Transaction Date: " . $transaction->getSettlementDate()->format('Y-m-d') . "\n"; echo "Amount: " . $transaction->getTransactionAmount() . "\n"; echo "Recipient: " . $transaction->getRecipientName1() . "\n"; echo "Reference: " . $transaction->getPresenterReference() . "\n"; }
// Transfer total $total = $transfer->getTotal(); echo "Total Amount: " . $total->getTotalAmount() . "\n"; echo "Sequence Number: " . $total->getSequenceNumber() . "\n";}All the fields, with their positions in the line: CFONB 240 objects.
How transfers are built
Section titled “How transfers are built”- a header line (
31) starts a new transfer. - a transaction line (
34) is added to the current transfer. - a total line (
39) is set on the current transfer, which is then added to the list and closed: the next transaction or total line needs a new header. - an empty line is ignored; a line of spaces or tabs throws a
ParseException.
So every transfer returned has a header and a total: getHeader() and getTotal() never throw on them. Any other order
throws a ParseException: a transaction or a total line outside a transfer (before the first header, or between a
total and the next header), a header line while a transfer is still open, a transfer still open at the end of the file:
Unable to attach the transaction with sequence number 2: missing "31" header record before itUnable to attach the total with sequence number 3: missing "31" header record before itUnable to start a transfer with the "31" header record of sequence number 1: the previous transfer has no "39" total recordUnable to close the transfer of the "31" header record of sequence number 1: missing "39" total record at the end of the fileAmounts
Section titled “Amounts”Transaction and total amounts are 12 digits in cents, read as floats with 2 decimals: 000000171200 is 1712.00.
Unlike CFONB 120 amounts, they have no sign: they are never negative. The reader does not check that the total is the
sum of the transactions.
Dates and strict mode
Section titled “Dates and strict mode”Dates are typed DateTimeInterface and are DateTimeImmutable objects at midnight, read from DDMMYY, like the
CFONB 120 dates: a date that does not exist, such as 320420, throws a ParseException.
getInitialTransactionSettlementDate() is null when blank or zero-filled (000000); the other dates are mandatory, and
000000 there throws a ParseException.
parse() accepts the same $strict argument as Cfonb120Reader::parse(), but no CFONB 240 rule depends on it:
malformed lines and records out of order always throw a ParseException.
Error handling
Section titled “Error handling”use Silarhi\Cfonb\Cfonb240Reader;use Silarhi\Cfonb\Exceptions\ParseException;
$reader = new Cfonb240Reader();
try { $content = file_get_contents('/path/to/cfonb240-file.txt');
foreach ($reader->parse($content) as $transfer) { echo "Processing transfer from: " . $transfer->getHeader()->getRecipientBankCode1() . "\n";
// Process transactions... }} catch (ParseException $e) { echo "Failed to parse CFONB file: " . $e->getMessage() . "\n";}HeaderUnavailableException and TotalUnavailableException are only thrown by a Transfer built by hand, without
setHeader() or setTotal(): see exceptions.
