Skip to content

Finalize sales invoice

Makes a concept sales invoice final. Without --save it only shows which invoice. With --save it changes data in Twinfield, and this can’t be undone: a final invoice can’t be changed anymore, and Twinfield books the sales transaction in the general ledger.

Twinfield documentation: Sales invoices.

finalize($invoiceType, $invoiceNumber) reads the invoice and saves it with status final; it throws a LogicException when the invoice is already final. The result has the booked transaction in $invoice->transaction, and salesTransaction($invoice) reads it.

From the sales transaction on, you can match a payment against it, see it in the open items and send reminders: see Sales invoice or sales transaction?.

To make an invoice final when you create it, call final() on the SalesInvoiceInput.

$twinfield = Example::twinfield();
$invoices = $twinfield->salesInvoices();
if (!isset($argv[1], $argv[2])) {
echo 'Pass the invoice type and number of a concept invoice, e.g. FACTUUR 12, and --save.', PHP_EOL;
exit(0);
}
$invoice = $invoices->get($argv[1], (int) $argv[2]);
if ($invoice->isFinal()) {
echo 'Invoice ', $invoice->invoiceType, ' ', $invoice->invoiceNumber, ' is already final.', PHP_EOL;
exit(0);
}
if (!in_array('--save', $argv, true)) {
echo 'Dry run; add --save to make concept invoice ', $invoice->invoiceType, ' ', $invoice->invoiceNumber,
' (', $invoice->totals->valueInc, ') final. This can not be undone.', PHP_EOL;
exit(0);
}
try {
// Final: no changes anymore, and Twinfield books the sales transaction.
$invoice = $invoices->finalize($invoice->invoiceType, $invoice->invoiceNumber);
} catch (ProcessXmlException $exception) {
foreach ($exception->result->messages() as $message) {
echo $message->type->value, ' at ', $message->path, ': ', $message->text, PHP_EOL;
}
exit(1);
}
$transaction = $invoices->salesTransaction($invoice);
echo 'Final: ', $invoice->invoiceType, ' ', $invoice->invoiceNumber, PHP_EOL;
echo 'Booked: ', $transaction->dayBook(), ' ', $transaction->number(), ', ', $transaction->value(),
', due ', $transaction->dueDate->format('Y-m-d'), PHP_EOL;
Terminal window
php examples/sales-invoices/finalize-sales-invoice/example.php FACTUUR 12
Dry run; add --save to make concept invoice FACTUUR 12 (151.25) final. This can not be undone.

Add --save to make it final:

Terminal window
php examples/sales-invoices/finalize-sales-invoice/example.php FACTUUR 12 --save

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: Sales invoices: Make final.

$twinfield->salesInvoices()->finalize('FACTUUR', 12);

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

<salesinvoice>
<header>
<office>1000</office>
<invoicetype>FACTUUR</invoicetype>
<invoicedate>20261103</invoicedate>
<duedate>20261203</duedate>
<bank>BNK</bank>
<customer>1000</customer>
<period>2026/11</period>
<currency>EUR</currency>
<invoicenumber>12</invoicenumber>
<status>final</status>
<paymentmethod>bank</paymentmethod>
<invoiceaddressnumber>1</invoiceaddressnumber>
<deliveraddressnumber>1</deliveraddressnumber>
</header>
<lines>
<line id="1">
<article>1</article>
<subarticle>1</subarticle>
<quantity>1</quantity>
<units>1</units>
<unitspriceexcl>125.00</unitspriceexcl>
<vatcode>VH</vatcode>
<allowdiscountorpremium>true</allowdiscountorpremium>
</line>
</lines>
</salesinvoice>

Twinfield answers with this XML in ProcessXmlStringResult:

<salesinvoice result="1">
<header>
<office>1000</office>
<invoicetype>FACTUUR</invoicetype>
<invoicedate>20261109</invoicedate>
<duedate>20261209</duedate>
<bank>BNK</bank>
<customer>1000</customer>
<period>2026/11</period>
<currency>EUR</currency>
<status>final</status>
<paymentmethod>bank</paymentmethod>
<invoiceaddressnumber>1</invoiceaddressnumber>
<deliveraddressnumber>1</deliveraddressnumber>
<headertext/>
<footertext/>
<invoicenumber>12</invoicenumber>
</header>
<lines>
<line id="1">
<article>510</article>
<subarticle>501.01</subarticle>
<quantity>2.00</quantity>
<units>1</units>
<unitspriceexcl>41.97</unitspriceexcl>
<vatcode name="VH" shortname="VH">VH</vatcode>
<allowdiscountorpremium>true</allowdiscountorpremium>
<description>Trousers</description>
<performancedate/>
<performancetype/>
<freetext1/>
<freetext2/>
<freetext3/>
<dim1 name="Omzet" shortname="Omzet">8020</dim1>
<valueexcl>83.94</valueexcl>
<vatvalue>17.63</vatvalue>
<valueinc>101.57</valueinc>
</line>
</lines>
<totals>
<valueexcl>83.94</valueexcl>
<valueinc>101.57</valueinc>
</totals>
<vatlines>
<vatline>
<vatcode name="VH" shortname="VH" percentage="21.000000000">VH</vatcode>
<vatvalue>17.63</vatvalue>
<performancetype/>
<performancedate/>
</vatline>
</vatlines>
<financials>
<code>VRK</code>
<number>202600013</number>
</financials>
</salesinvoice>

finalize() reads the concept invoice first, and sends it again with status final.

View examples/sales-invoices/finalize-sales-invoice/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.