Skip to content

Developer notes

app/addons/kv_invoice_builder/
addon.xml settings, tables, document + e-mail templates (resources/)
init.php / func.php hook registration and handlers
resources/document_templates.xml document types kv_ib_invoice, kv_ib_credit_note (code "default") + snippets
resources/email_templates.xml e-mail kv_invoice_builder_document
controllers/backend/kv_invoice_builder.php lists, view/PDF, credit notes, bulk tools, vendor settings, CSV
controllers/backend/orders.post.php "Invoices" tab on the order page
controllers/frontend/kv_invoice_builder.php customer document page / PDF
controllers/frontend/orders.post.php links on the customer's order page
schemas/documents/ types.post.php, kv_ib_invoice.php, kv_ib_credit_note.php, order.post.php
schemas/menu, permissions, context_menu, settings
Tygh/Addons/KvInvoiceBuilder/
Issuer.php numbering transaction, invoices and credit notes
Series.php series keys, formats, vendor settings
Snapshot.php frozen content of an invoice Credit.php credit note content
Documents.php storage (read side), lists View.php escaped output values
Output.php HTML, print page, PDF, ZIP Mail.php e-mail attachments and sending
Hooks.php automatic triggers Access.php admin, vendor and customer access
Document/Type.php, Document/Context.php core document type classes
License/, Upgrade/ licence client, self-healing upgrades
design/backend/templates/addons/kv_invoice_builder/ admin views, order hooks, settings info
var/themes_repository/responsive/templates/addons/kv_invoice_builder/hooks/orders/details_tools.post.tpl

A number is taken inside one database transaction:

  1. SELECT last_no FROM ?:kv_ib_counters WHERE series_key = … FOR UPDATE locks the counter row of the series (the row is created when the series is new). Parallel issues in the same series wait here.
  2. The idempotency key of the document (inv:<order>, full:<invoice>, rma:<return>, wallet:<ledger entry>) is checked under the lock; credit notes also re-check the open amount of the invoice.
  3. The document is inserted with last_no + 1 and the counter is moved; then the transaction commits.

Any failure rolls the transaction back, so no number is lost or used twice. Deadlocks and lock wait timeouts are retried (6 attempts with back-off). Unique keys on (series_key, seq_no), inv_order_id and source_key are the last line of defence.

Series key: I|C + scope (s<company> storefront on CS-Cart, v<company> vendor or m marketplace on Multi-Vendor) + year (or 0 without the yearly reset). Changing a format does not start a new series.

Table Contents
?:kv_ib_documents One row per invoice (doc_type I) or credit note (C): order, company, series, sequence, number, issue time, invoice link, source and idempotency key, totals in the base currency, customer, data (JSON snapshot: seller, buyer, lines, taxes, totals, currency with its rate, texts)
?:kv_ib_counters Last number per series
?:kv_ib_vendor_settings Multi-Vendor: own formats, tax ID, notes and footer per vendor

Documents are never updated after insert, except the order_deleted flag when an order is deleted (the documents stay).

kv_ib_invoice and kv_ib_credit_note are registered in schemas/documents/types.post.php and as template.document.<type>.type services (ServiceProvider). Both implement IType and IPreviewableType; variables are defined in schemas/documents/kv_ib_invoice.php (d, s, b, escaped by View). Templates are valid in Twig 2.14 (CS-Cart 4.17/4.18) and Twig 3.11 (4.21). Loops live in snippets because the visual editor of Design → Documents moves Twig tags placed between table rows.

schemas/documents/order.post.php adds kv_invoice (number, date, credit_notes) to the core order documents.

Render a document from code:

$doc = \Tygh\Addons\KvInvoiceBuilder\Documents::invoiceForOrder($order_id);
$html = \Tygh\Addons\KvInvoiceBuilder\Output::html($doc); // document HTML
$file = \Tygh\Addons\KvInvoiceBuilder\Output::pdfFile([$doc]); // '' when PDF is not available

kv_invoice_builder_document_issued($doc) fires once after a new invoice or credit note was issued and committed (status change, manual issue, bulk issue, return, wallet refund, manual credit note). $doc is the decoded document (doc_type I or C, doc_id, order_id, invoice_id, number, total, data). Register it like a core hook (fn_register_hooks('kv_invoice_builder_document_issued'), handler fn_<your_addon>_kv_invoice_builder_document_issued($doc)); errors thrown by a handler are logged and never undo the document. Since 1.0.1.

change_order_status (issue invoice / full credit note, before the status e-mails), rma_update_details_post (returns), kv_customer_wallet_post_entry_post (wallet refunds), delete_order (flag documents), get_orders_post (invoice column), mailer_send_pre (attach documents issued in this request to the customer’s status e-mail), template_document_get_name (document names). All handlers are store-safe: errors are logged (general/runtime) and never interrupt the order flow.

Nothing is printed on cacheable pages. Customer output appears on the order details page and the document page, which are session pages, and the document page sends Cache-Control: private, no-store.

The licence client (License\Client) verifies the key for the store’s domain once a day, only in the admin panel or from cron. Without a valid license no new numbers are issued (status changes, manual and bulk issue, returns, wallet refunds) and nothing is attached to e-mails; lists, print views, PDF, ZIP and CSV of issued documents keep working.