Skip to content

Bulk import with errors

Saves three bank transactions in one request with saveMany(), where the second one has an account that does not exist. This changes data in Twinfield; the example deletes the saved transactions again.

Twinfield documentation: Bank transactions.

Twinfield saves the valid transactions and refuses the others, so saveMany() does not throw: it returns a SaveManyResult with, per input in the same order, a SavedTransaction (with the transaction) or a FailedTransaction (with the errors and where they are). Use saved(), failed() and allSaved() to handle them.

$twinfield = Example::twinfield();
$bank = $twinfield->bankTransactions();
$costs = Example::profitAndLossAccount($twinfield);
$base = BankTransactionInput::forBankBook(Example::bankBook($twinfield), new DateTimeImmutable());
// Three statements in one request; the second has an account that does not exist.
$result = $bank->saveMany(
$base->statement(1)->pay($costs, '1.00', description: 'Example day 1'),
$base->statement(2)->pay('NOPE9999', '2.00', description: 'Example day 2'),
$base->statement(3)->pay($costs, '3.00', description: 'Example day 3'),
);
foreach ($result as $item) {
echo sprintf('[%d/%d] ', $item->index + 1, count($result)), match (true) {
$item instanceof SavedTransaction => 'saved as ' . $item->transaction->number(),
default => 'failed: ' . $item->message(),
}, PHP_EOL;
}
echo count($result->saved()), ' saved, ', count($result->failed()), ' failed', PHP_EOL;
// Clean up: delete the provisional example transactions again.
foreach ($result->saved() as $saved) {
$bank->delete($saved->transaction->dayBook(), $saved->transaction->number(), 'twinfield-php example');
}
Terminal window
php examples/bank-transactions/bulk-import-with-errors/example.php
[1/3] saved as 202600049
[2/3] failed: transactions/transaction[2]/lines/line[2]/dim1: Grootboekrekening NOPE9999 bestaat niet.
[3/3] saved as 202600050
2 saved, 1 failed

What the SDK sends to Twinfield and what Twinfield sends back, for a call like the one in this example. The responses come from the tests, with example data.

Twinfield documentation: Bank transactions: Create.

$base = BankTransactionInput::create('BNK', '1010', new DateTimeImmutable('2026-11-03'));
$twinfield->bankTransactions()->saveMany(
$base->receive('1300', '50.00', customer: '1000'),
$base->receive('1300', '75.00', customer: '9999'),
);

The SDK sends this XML as xmlRequest of ProcessXmlString to processxml.asmx:

<transactions>
<transaction destiny="temporary">
<header>
<office>1000</office>
<code>BNK</code>
<date>20261103</date>
</header>
<lines>
<line type="total" id="1">
<dim1>1010</dim1>
<debitcredit>debit</debitcredit>
<value>50.00</value>
</line>
<line type="detail" id="2">
<dim1>1300</dim1>
<dim2>1000</dim2>
<debitcredit>credit</debitcredit>
<value>50.00</value>
</line>
</lines>
</transaction>
<transaction destiny="temporary">
<header>
<office>1000</office>
<code>BNK</code>
<date>20261103</date>
</header>
<lines>
<line type="total" id="1">
<dim1>1010</dim1>
<debitcredit>debit</debitcredit>
<value>75.00</value>
</line>
<line type="detail" id="2">
<dim1>1300</dim1>
<dim2>9999</dim2>
<debitcredit>credit</debitcredit>
<value>75.00</value>
</line>
</lines>
</transaction>
</transactions>

Twinfield answers with this XML in ProcessXmlStringResult:

<transactions result="0">
<transaction destiny="temporary" result="1" location="temporary">
<header>
<office name="Example Holding B.V." shortname="Holding">1000</office>
<code name="Bank" shortname="Bank">BNK</code>
<currency name="Euro" shortname="Euro">EUR</currency>
<date>20261103</date>
<period>2026/11</period>
<number>202600047</number>
<user name="Example user" shortname="User">EXAMPLE</user>
<startvalue>0.00</startvalue>
<closevalue>0.00</closevalue>
</header>
<lines>
<line type="total" id="1">
<dim1 name="Bank" type="BAS">1010</dim1>
<debitcredit>debit</debitcredit>
<value>50.00</value>
<rate>1</rate>
<basevalue>50.00</basevalue>
<matchstatus>notmatchable</matchstatus>
</line>
<line type="detail" id="2">
<dim1 name="Debiteuren" type="BAS">1300</dim1>
<dim2 name="Example customer" type="DEB">1000</dim2>
<debitcredit>credit</debitcredit>
<value>50.00</value>
<rate>1</rate>
<basevalue>50.00</basevalue>
<matchstatus>available</matchstatus>
</line>
</lines>
</transaction>
<transaction destiny="temporary" result="0">
<header>
<office name="Example Holding B.V." shortname="Holding">1000</office>
<code name="Bank" shortname="Bank">BNK</code>
<currency name="Euro" shortname="Euro">EUR</currency>
<date msgtype="warning" msg="Datum valt buiten de periode.">20261103</date>
</header>
<lines>
<line type="total" id="1">
<dim1>1010</dim1>
<debitcredit>debit</debitcredit>
<value>75.00</value>
</line>
<line type="detail" id="2">
<dim1>1300</dim1>
<dim2 msgtype="error" msg="Relatie 9999 bestaat niet.">9999</dim2>
<debitcredit>credit</debitcredit>
<value>75.00</value>
</line>
</lines>
</transaction>
</transactions>

View examples/bank-transactions/bulk-import-with-errors/example.php on GitHub.

Made by SolveBeam

Twinfield and Wolters Kluwer are trademarks of Wolters Kluwer. Their names and logos are used only to refer to their products.