Saving an invoice — process and rules

The steps of creating and editing an invoice: quota, validation, duplicate check, invoice block locking, the shared insert path, read-back and permissions.

Saving an invoice runs through one endpoint (finance/invoice.save); the invoice_id decides between create and update. Every insert uses the shared addInvoice() path.

Data model

Table names carry the installation's own prefix in the database.

TableRole
invoice, invoice_itemheader and items (written by Dlbtinvoice::add())
invoice_retentionretention rows (saveRetentions())
invoice block tablesthrough the masterdata/invoice_block model, with number locking
allocation, label_linkanalytics and labels (on update)

Create process

  1. Right: canSave(route, 0) — if the add key is not in the feature master, edit decides; the owner (company_client_id) must be in scope (ownerInScope).
  2. Quota: dlbtquota->checkAction(user, route, 'add', 1); states need_credit / need_plan give an error (error_quota_*).
  3. Field validation, duplicate search (findDuplicateInvoice; a separate message for a duplicate in the trash), for storno/correcting invoices the referenced invoice is required and must exist, offset check (checkOffsetInvoice), retention amount limit.
  4. For own numbered outgoing invoices: lock the series (lockSeries), build the number, check date order.
  5. addInvoice($post), then read back (getInvoice); if missing: error_not_created, the number is not consumed.
  6. commitSeriesSeq, set the invoice kind (applyKind()), consume quota (consumeAction()), save retentions, write the log.
  7. The response returns the new invoice_id; there is no redirect.

Update

Same validation; if the invoice is numbered/finalised (getSeriesLock()['numbered']), error_invoice_finalized. Otherwise editInvoice($id, $post): full replacement of header and items, line recalculation in the library, applyKind(), retentions, log. Opening the form also checks canSave and ownerInScope: another company's invoice cannot be opened.

Error keys

error_supplier, error_customer_required, error_currency, error_date, error_invoice_no_form, error_number_in_own_series, error_duplicate, error_duplicate_trashed, error_offset_*, error_retention_over, error_reference_*, error_number_client, error_number_busy, error_number_block, error_series_prefix_taken, error_number_date_order, error_quota_*.

Form helpers

  • getDueDate, getTaxData, getRate — due date, VAT rate, MNB exchange rate; right: add or edit.
  • offsetinvoices — outgoing invoices available for offset; right: edit + owner scope.
  • docsearch, attachdocument, detachdocument, unlinkdocument — search a document-centre document, link it to the invoice or unlink it, remove the old invoice_document link; right: edit + scope.
  • The PDF fields (template, clause, note) are bound to the pdf right.

Permissions

  • Layer 1: base access and modify right on the invoice path.
  • Layer 2: add (not classified in the feature master, only listed in the dependency tables), edit (BASE).
  • Layer 3: ownerInScope and invoiceInScope.