Skip to content

Daily cash register

The end-of-day report of a cash register becomes one cash transaction: the sales per VAT rate, the expenses paid from the till, and the difference between the cash count and the calculated balance.

Twinfield documentation: Cash transactions.

The report has amounts including VAT; the example splits them into amount and VAT with bcmath and books them with receive() and pay(). The closing balance must be what was counted, so a difference goes to a cash differences account.

Change the accounts and VAT codes at the top to those of your administration. By default it is a dry run; add --save to create the transaction as provisional. Pass the counted amount as first argument, e.g. 940.00.

$twinfield = Example::twinfield();
// The end-of-day report of the cash register: sales per VAT rate, including VAT.
$sales = [
['account' => '8000', 'vatCode' => 'VH', 'rate' => '21', 'gross' => '605.00', 'text' => 'Sales 21% VAT'],
['account' => '8010', 'vatCode' => 'VL', 'rate' => '9', 'gross' => '109.00', 'text' => 'Sales 9% VAT'],
];
$expenses = [
['account' => '4300', 'vatCode' => 'IH', 'rate' => '21', 'gross' => '24.20', 'text' => 'Coffee and milk'],
];
$opening = Amount::of('250.00');
$counted = Amount::fromString($argv[1] ?? '939.50');
// Amount without VAT: gross × 100 / (100 + rate), rounded half up to cents; and the VAT.
$split = static function (Amount $gross, Amount $rate): array {
$net = Amount::of(bcadd(bcdiv(bcmul($gross->value, '100', 6), bcadd('100', $rate->value, 6), 6), '0.005', 2));
return [$net, $gross->minus($net)];
};
$input = CashTransactionInput::create('KAS', '1000', new DateTimeImmutable('2026-11-03'))
->statement(311, startValue: $opening)
->description('Cash register 3 November');
foreach ($sales as $line) {
[$net, $vat] = $split(Amount::of($line['gross']), Amount::of($line['rate']));
$input = $input->receive(
$line['account'],
$net,
vatCode: $line['vatCode'],
vatValue: $vat,
description: $line['text'],
);
echo sprintf('+ %8s = %8s + VAT %6s %s', $line['gross'], $net, $vat, $line['text']), PHP_EOL;
}
foreach ($expenses as $line) {
[$net, $vat] = $split(Amount::of($line['gross']), Amount::of($line['rate']));
$input = $input->pay($line['account'], $net, vatCode: $line['vatCode'], vatValue: $vat, description: $line['text']);
echo sprintf('- %8s = %8s + VAT %6s %s', $line['gross'], $net, $vat, $line['text']), PHP_EOL;
}
// The cash count decides the closing balance; book the difference.
$difference = $counted->minus($input->closeValue() ?? $opening);
if (!$difference->isZero()) {
$input = $difference->isNegative()
? $input->pay('8990', $difference->absolute(), description: 'Cash difference')
: $input->receive('8990', $difference, description: 'Cash difference');
echo 'Cash difference: ', $difference, PHP_EOL;
}
echo 'Cash: ', $opening, ' → ', $input->closeValue(), ' (counted ', $counted, ')', PHP_EOL;
if (!in_array('--save', $argv ?? [], true)) {
echo PHP_EOL, 'Dry run; add --save to create it as provisional transaction:', PHP_EOL, PHP_EOL;
echo Example::xml($input->toXml($twinfield->companyCode() ?? '')), PHP_EOL;
exit(0);
}
$transaction = $twinfield->cashTransactions()->create($input);
echo 'Created provisional transaction ', $transaction->dayBook(), ' ', $transaction->number(), PHP_EOL;
Terminal window
php examples/cash-transactions/daily-cash-register/example.php
+ 605.00 = 500.00 + VAT 105.00 Sales 21% VAT
+ 109.00 = 100.00 + VAT 9.00 Sales 9% VAT
- 24.20 = 20.00 + VAT 4.20 Coffee and milk
Cash difference: -0.30
Cash: 250.00 → 939.50 (counted 939.50)
Dry run; add --save to create it as provisional transaction:
<transaction destiny="temporary">
<header>
<office>1002</office>
<code>KAS</code>
<date>20261103</date>
<statementnumber>311</statementnumber>
<startvalue>250.00</startvalue>
<closevalue>939.50</closevalue>
</header>
<lines>
<line type="total" id="1">
<dim1>1000</dim1>
<debitcredit>debit</debitcredit>
<value>689.50</value>
<description>Cash register 3 November</description>
</line>
<line type="detail" id="2">
<dim1>8000</dim1>
<debitcredit>credit</debitcredit>
<value>500.00</value>
<description>Sales 21% VAT</description>
<vatcode>VH</vatcode>
<vatvalue>105.00</vatvalue>
</line>
<line type="detail" id="3">
<dim1>8010</dim1>
<debitcredit>credit</debitcredit>
<value>100.00</value>
<description>Sales 9% VAT</description>
<vatcode>VL</vatcode>
<vatvalue>9.00</vatvalue>
</line>
…

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: Cash transactions: Create.

$twinfield->cashTransactions()->create(
CashTransactionInput::create('KAS', '1000', new DateTimeImmutable('2026-11-03'))
->statement(311, startValue: '250.00')
->description('Cash register 3 November')
->receive('8000', '500.00', vatCode: 'VH', vatValue: '105.00', description: 'Sales 21% VAT')
->receive('8010', '100.00', vatCode: 'VL', vatValue: '9.00', description: 'Sales 9% VAT')
->pay('4300', '20.00', vatCode: 'IH', vatValue: '4.20', description: 'Coffee and milk'),
);

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

<transaction destiny="temporary">
<header>
<office>1000</office>
<code>KAS</code>
<date>20261103</date>
<statementnumber>311</statementnumber>
<startvalue>250.00</startvalue>
<closevalue>939.80</closevalue>
</header>
<lines>
<line type="total" id="1">
<dim1>1000</dim1>
<debitcredit>debit</debitcredit>
<value>689.80</value>
<description>Cash register 3 November</description>
</line>
<line type="detail" id="2">
<dim1>8000</dim1>
<debitcredit>credit</debitcredit>
<value>500.00</value>
<description>Sales 21% VAT</description>
<vatcode>VH</vatcode>
<vatvalue>105.00</vatvalue>
</line>
<line type="detail" id="3">
<dim1>8010</dim1>
<debitcredit>credit</debitcredit>
<value>100.00</value>
<description>Sales 9% VAT</description>
<vatcode>VL</vatcode>
<vatvalue>9.00</vatvalue>
</line>
<line type="detail" id="4">
<dim1>4300</dim1>
<debitcredit>debit</debitcredit>
<value>20.00</value>
<description>Coffee and milk</description>
<vatcode>IH</vatcode>
<vatvalue>4.20</vatvalue>
</line>
</lines>
</transaction>

Twinfield answers with the saved transaction, with its number and the names of the codes, in the same format as Read cash transaction.

View examples/cash-transactions/daily-cash-register/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.