Panel Invoice Screen Hooks
The twenty-three placement points of the invoice, cash book and coupon screens: the invoice detail, summary cards, list toolbars and forms.
Overview
The placement points of the invoice, cash book and coupon screens live here: the sections of the invoice detail, the list toolbars and the coupon and currency forms.
The invoice summary point hands you the arithmetic already done: the balance left and the total paid. In the cash summary each value arrives in two forms; use the raw one when you calculate.
Reference
A badge or button in the invoice title
Appears in the title row of the invoice detail. It suits its counterpart in your accounting system or a shortcut of your own.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_detail.header_actions', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The top of the invoice summary card
Appears at the very top of the summary card. The balance left and the total paid are in your hands.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoice_detail.summary_card_top', 10,
function ($invoice, $invoice_id, $balance, $total_paid) {
// Kismi odemede de bakiye dolu gelir.
if ($balance <= 0 || $total_paid <= 0) return null;
return '<div class="alert alert-info">Kismi odeme</div>';
});The bottom of the invoice summary
Appears after the lines of the summary card.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_detail.summary.bottom', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The top of the payments section
Appears above the list of payments recorded against the invoice.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_detail.payments.top', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});A new tab on the invoice detail
Runs once the invoice tabs are built. You do not return HTML: you add the tab through the object’s method.
Hook::add('ui:admin.invoice_detail.tabs', 10, function ($tab, $invoice) {
$tab->add('acme', 'Acme', Acme::renderTab((int) ($invoice['id'] ?? 0)));
});The very bottom of the invoice page
Appears at the very end of the page.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_detail.bottom', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Beside the invoice list summary cards
Appears beside the summary cards above the list. It puts a card of your own next to the core ones.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_list.stats', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Above the table on the invoice list
Appears immediately above the invoice table.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_list.before_table', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The invoice list toolbar
Appears in the toolbar of the invoice list.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoice_list.toolbar', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The cash book toolbar
Appears in the toolbar of the income and expense book.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_cash.toolbar', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Above the table on the cash book
Appears immediately above the cash book table.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_cash.before_table', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Above the table on the recurring expense list
Appears immediately above the recurring expense table.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_periodic_expenses.before_table', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The recurring expense toolbar
Appears in the toolbar of the recurring expense list.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_periodic_expenses.toolbar', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The coupon list toolbar
Appears in the toolbar of the coupon list.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.coupon_list.toolbar', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Above the table on the coupon list
Appears immediately above the coupon table.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.coupon_list.before_table', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The currency list toolbar
Appears in the toolbar of the currency list.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.currency_list.toolbar', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Above the table on the currency list
Appears immediately above the currency table.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.currency_list.before_table', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Below the cash book summary
Appears below the income and expense summary.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_cash.summary.after', 10, function ($summary) {
// Karsilastirmada HAM degeri kullanin, bicimli metni degil.
if ((float) ($summary['balance'] ?? 0) >= 0) return null;
return '<div class="alert alert-warning">Kasa eksi bakiyede</div>';
});Above the invoice creation form
Appears above the manual invoice form.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_edit.before_content', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Below the invoice creation form
Appears below the same form.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.invoices_edit.bottom', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Above the coupon form
Appears above the coupon add and edit form.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.coupon_edit.before_content', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Below the coupon form
Appears below the same form.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.coupon_edit.bottom', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});The coupon summary column
Appears in the summary column beside the coupon form.
null and false are skipped, so return null when you have nothing to add. The output enters the page unescaped — clean anything coming from outside yourself.Hook::add('ui:admin.coupon_edit.summary_sidebar', 10, function () {
return '<div class="alert alert-info">Acme</div>';
});Pitfalls
In the cash summary every amount exists in two forms: a raw number and the formatted text meant for the screen. That text carries a thousands separator and a currency symbol; a listener converting it back to a number gets the wrong answer on every amount large enough to have a separator.
The balance left in the invoice summary is also filled on a partial payment. A listener assuming "a balance means no payment" treats a half-paid invoice as unpaid. Look at the total paid as well.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.