The Generic Document
Somebody teaches you how a cash bill is numbered, and without anyone saying so you have also just learned how a purchase invoice, a sales return, a delivery order, a quotation, a receipt voucher and a credit note are numbered. That is not a coincidence and it is not a convention. In BigLedger those are all the same record in the same table, and only one column says which kind of document you are looking at.
Nothing on any screen tells you this. Every applet presents itself as its own thing, with its own name on the tab. This page is the part underneath: what is shared, what follows from it for you, and the one place the sharing gets genuinely confusing — the ten different numbers a single document can be carrying at once.
Fifteen to twenty minutes, and most people come back to the numbering section rather than read it straight through. There is nothing to set up.
One table, many kinds of document
Every financial document in BigLedger is a row in bl_fi_generic_doc_hdr with its lines in
bl_fi_generic_doc_line. What makes one a cash bill and another a purchase invoice is the value in
server_doc_type — INTERNAL_SALES_CASHBILL, INTERNAL_PURCHASE_INVOICE, and so on. The
developers’ own note on that column describes it as recording “the classname” of the document, which
is exactly the right way to think about it: one shape, many kinds.
You can see how deliberate this was. The comment on the table lists the twelve separate tables of the predecessor system it replaced — a supplier invoice index, a customer invoice index, a credit memo index, a payment voucher index, a receipt index, a consignment index, a delivery order index, a job sheet index, a sales order index, a goods received index, a purchase return index. Twelve tables became one. Today the registry that binds each document type to its own validation rules and permissions holds 97 types — and three narrower lists elsewhere in the code hold 68, 79 and 51, which is a good reason not to trust any published list of document types, ours included, over what your own tenant actually contains.
What that buys you is the thing worth taking away. A knock-off between a sales order and an invoice, a contra between an invoice and a credit note, a report that cuts sales and purchases together, an approval route that works on any document — none of those needs to know what kind of document it is holding. Neither do you. When you learn that finalising is what assigns the company and branch numbers, you have learned it for all of them at once.
Across the fleet this is one of the busiest tables in the product: about 12 million document headers and 36 million document lines, held by 75 of 90 tenants (measured 2026-09-16, aggregates only).
This is not the same as an external generic document. bl_fi_xtn_generic_doc_hdr is a
separate family, for a document that originated outside your tenant and is being matched against
yours — a supplier’s own invoice, a warehouse operator’s goods receipt. Those never post a journal,
never move stock and have no posting status. If a page or a screen says “external”, check which
family it means.
Your document can be carrying ten different numbers
Here is the part that surprises people. A single document header has five server_doc_ columns
and five client_doc_ columns, all of them free-form text up to 255 characters, all of them
allowed to be empty. They are two different ideas that happen to look alike:
server_doc_*is what BigLedger called this document.client_doc_*is what somebody else called it.
The five BigLedger assigns
These are running numbers, drawn from the platform’s single counter table. Every one of them is a read-only field on the form — you never type them and you cannot change them.
| Column | What it counts | Label you see | When it appears | How much it is used |
|---|---|---|---|---|
server_doc_1 | one sequence per document type, for the whole tenant | Doc No (Tenant) | the moment the document is created, draft or not | every document, in 74 of 90 tenants |
server_doc_2 | the same, per company | Doc No (Company) | only when the document is finalised | 73 of 90 tenants |
server_doc_3 | the same, per branch | Doc No (Branch) | only when the document is finalised | 73 of 90 tenants |
server_doc_location | the same, per company and location | no label; never shown | only when the document is finalised | not measured; nothing reads it outward |
server_doc_4 | the same, per point-of-sale device | no label; never shown | at finalise, and only if the document carries a device | 3 tenants, heavily |
server_doc_5 | not established | no label; never shown | never, as far as we can find | 2 rows in the entire fleet |
server_doc_1 is the one to reach for. It exists from the first save, it is the default column the
listing search box matches on, and it is what the rest of the product means when it says “the
document number”. The company and branch numbers are hidden by default on most listings and are
switched on per applet — each has a setting that hides it and a permission that puts it back, which
is why two colleagues can be looking at the same listing and see different columns. See
Document Numbering for how, for why the sequence starts in the
millions, and for why no prefix, year or padding is stored. (A short code such as PI in front of a
number is added when the document is rendered, not held in the column.)
server_doc_4 and server_doc_5 are a different matter, and we are not going to invent a purpose
for them. Neither has a label, a column header, a hide setting or a permission anywhere in the
shipped applets — there is nothing to switch on. server_doc_4 is genuinely written, at finalise,
for a document that carries a point-of-sale device, and three tenants in the fleet use it heavily.
server_doc_5 is written by nothing: we searched every path that writes a server_doc_ column on
this table and it appears in none of them, and across all 90 tenants the column holds two rows.
The developers’ comment on it says “sublevel, example: device” — but the device number goes to
server_doc_4, so the comment ladder appears to have been written before server_doc_location was
inserted and never shifted down. Treat server_doc_5 as reserved. If you find data in it, something
outside the standard document services put it there.
One more honesty note: two code paths disagree about what server_doc_3 means. The path every
applet uses writes the branch number there. An older path, still used when a document is created
header-only, writes the company-and-location number instead. It is recorded as an open question
rather than smoothed over, because a report that groups on server_doc_3 is grouping on two
different things depending on how the document was born.
The five your own system fills in
These are not running numbers and BigLedger does not generate them. They are yours. The developers' comment on the first of them is the clearest statement of intent in the whole schema:
"
server_doc_1from client in case of peer to peer data sync" — the column comment onclient_doc_1
In other words: client_doc_1 is the other system’s server_doc_1. When a document arrives from
somewhere else it keeps the number it already had, in the client columns, and gets a BigLedger number
of its own in the server columns. Two identities on one record, neither overwriting the other.
The family is wider than five columns: client_doc_type ("server_doc_type of client server"),
client_guid (“GUID of the client server”), client_source — a free-text mark of which system it
came from — and, easy to miss, five matching dates (client_doc_date_01 to _05) and five matching
statuses (client_doc_status_01 to _05). If your integration needs to record not only the other
system’s number but when it was raised there and what state it is in, the columns already exist.
Almost nobody uses them.
client_doc_2 through client_doc_5 carry no column comment at all, and no code path reserves
them for anything. Whatever a caller sends in them is stored as sent and given back unchanged, and
all five are searchable — exactly, partially, or as a set. Which means the honest answer to “what is
client_doc_4 for?” is: whatever the person who built your integration decided.
Two things about them are worth knowing before you claim one for yourself.
They are already in use, and not in the order you would guess. Measured across all 90 tenants:
client_doc_1 is populated in 20 tenants, but client_doc_2 in 42, client_doc_3 in 40 and
client_doc_5 in 41. The column the schema documents as the mirror of the other system’s number is
the least used of the five. Something is filling the middle and last slots across half the fleet,
and it is not the platform. So if you are about to start putting a warehouse reference in
client_doc_4, check what is already there. Nothing will stop you and nothing will warn you.
A workflow can give one of them a fixed meaning, and that meaning is local to the workflow, not to
the column. The clearest live example is AP automation’s supplier-document matching, which reads a
purchase order’s number out of client_doc_1, the referenced purchase order out of client_doc_2
and the supplier’s delivery-order number out of client_doc_3 — see
AP Automation. A goods received note keyed
without client_doc_3 can never match there. Marketplace connectors do the same thing more quietly:
they write the marketplace’s own order number into client_doc_1 on every order they bring in.
And one exception to “BigLedger never writes these”. For e-invoicing, if client_doc_1 is empty
and your company has a client-document format configured, the platform composes one itself — branch
description, document-type code, the year and month of the transaction date, and the branch running
number, joined with hyphens — and writes it back onto the document. That is the only case we
found of the platform filling a client column, and it exists because the regulator wants a
human-readable reference and a bare running number is not one.
client_doc_1 also exists on the document line, not only the header, where it is the key an
imported line is matched back by. The line table even carries a dedicated index for it. You will meet
it on the file-import checking screens as Line Client Doc 1. The server columns have no line-level
equivalent at all: a document has one set of BigLedger numbers, but its lines can each carry their
own foreign reference.
Which number the bank, the auditor or the tax authority actually sees
Not the same one, and that surprises people who assume the document number is a fact about the document rather than a setting.
A bank or an auditor sees server_doc_1. That is not a convention we are recommending, it is
what the product does: the bank reconciliation report identifies each document by its short code
plus server_doc_1, one report query literally aliases the column server_doc_no, and the e-invoice
PDF’s default filename is built from it. If somebody reconciling your account asks for “the document
number”, this is the one.
LHDN sees something else entirely, and this catches people out. The identifier on the tax
document is a separate running number of its own, held on the e-invoice record rather than on
your document, and it is what becomes the invoice ID in the file sent to the regulator. Your
server_doc_1 reaches LHDN in three other places: as the fallback invoice ID when that running
number is absent (as INTERNAL_SALES_INVOICE-1000583, document type and number joined), as the ID on
every invoice line, and on the additional-document reference. You can also compose the running number
from a template, and the template accepts server_doc_1, server_doc_2, server_doc_3,
doc_reference and all five client columns — but not server_doc_4, server_doc_5 or
server_doc_location, which can never reach the regulator by any route. Details on
My E-Invoice Admin.
On paper, the number is whatever the printable format prints. The format chooses the column and
the heading, and it can print a branch code beside the number, so a branch’s bill can read
KV01 / 1000017 while the tenant number stays the audit trail underneath. This is where a prefix
lives, if you want one — see Printable Formats. Even client_doc_4
and client_doc_5 have print tokens of their own, so a foreign reference can be printed as the
customer-facing number.
To a shopper looking up their own receipt on a storefront, the field shown as the invoice number
is a widget parameter that accepts server_doc_1, any client column, doc_reference or the e-invoice
running number, and defaults to server_doc_1.
einvoice_number is none of the above. It is assigned by LHDN’s system, not by BigLedger, and it
is the only identifier on the document that the tax authority itself issued.
If all of that feels like the platform declining to name one canonical number — it is, and it knows. The e-invoice search screen binds a single input, named server or client doc no, against all ten columns at once, because whichever one you have in your hand is the right one to search with.
The practical consequence: before you tell a bank, an auditor or a customer “quote your document
number”, find out which column your printable format is actually printing. If the answer is
server_doc_3, that number repeats across branches and is meaningless without the branch beside it.
Why there are two numbering systems at all
Because for a large minority of BigLedger customers, most of their documents were never keyed by anyone in their office.
Measured across all 90 tenants on 2026-09-17: 38 of them hold documents stamped with an external
client_source — 6.2 million documents, a median of 26,000 per tenant. The remaining 68 hold
6.0 million documents with no source mark. About half of every document in the fleet arrived from
somewhere else: a system still running alongside BigLedger during a migration, a marketplace, a
webstore, a mall’s own point of sale, or a historical import done at go-live.
Those documents already had numbers. Their staff quote those numbers on the phone; their customers have them printed on receipts; their old reports are indexed by them. Throwing them away to adopt a BigLedger number is not a data-modelling decision, it is a business disruption. Keeping only the old number instead would break everything on our side that assumes a running sequence.
So the platform declines to choose. The document gets both, in two column families that never collide, and an imported document is otherwise an entirely first-class document: same table, same journal engine, same e-invoice queue, same posting status as one a clerk typed. That is what makes a partial migration possible at all — you can hand BigLedger one obligation, keep the rest where it is for a year, and still have both sets of books agree on which document is which.
What else is the same on every document type
Four more behaviours you learn once. Each has its own page; this is the map, not the territory.
A signum decides whether a document adds or subtracts. Two columns, amount_signum on the
header and line and quantity_signum on the line, hold +1 or -1, and they are what make one
document type increase a balance where another decreases it. The developers state it plainly: a
customer invoice is +1, a receipt -1, a supplier invoice -1, a payment voucher +1, a credit
note -1, a debit note +1; on quantity, a goods received note and a sales return are +1 because
they bring stock in, an invoice and a purchase return -1 because they take it out. This is why the
sign of an amount on screen is not always the sign in the ledger, and why a document type cannot be
“reversed” simply by typing a negative.
Posting status is what finalising changes, and finalising is not saving. A document sits at
draft until it is finalised, and FINAL is the event, not the save button. Finalise is a separate
call that queues one primary job processor, which fans out to secondary processors posting into the
journal, inventory, the cashbook, membership points and the e-invoice queue. Your screen comes back
before any of that has finished — deliberately, so that a till is never made to wait for a tax
submission. It is also why the company and branch numbers appear a moment after you finalise rather
than the instant you click. Where that chain leaves stored figures behind is unpacked in
Where this number came from.
Branch and profit centre sit on the header and on every line, independently. guid_branch and
guid_profit_center exist in both places, which means one document can carry lines belonging to
different branches. The developers’ comment on the header’s is “the billing branch”; on the line it is
“the billing branch, to account for sales for reporting purpose” — two different jobs. guid_store
is a third, separate dimension: “stocks are deducted from this location”. Branch is not location and
neither is profit centre, and a chart of accounts that encodes branch into the GL code is fighting a
column that already exists.
Costing is written back onto the line after the fact. When the chain has run, the moving-average,
weighted-average, FIFO and LIFO costs of each line are computed from the inventory ledger and written
back onto bl_fi_generic_doc_line — all four, side by side, so a report can read the basis a company
uses without recomputing anything. It also means the cost on a line you finalised last quarter is a
stored figure that a later backdated purchase can rewrite. That is the single commonest cause of
“last quarter’s margin changed by itself”, and it is unpacked in
Where this number came from.
What goes wrong with document numbers
“The document number is blank on a finalised document”
The tenant number is assigned immediately after the document is created, and if that fails — a
database hiccup, a counter that could not be topped up — the error is logged and swallowed and the
document saves anyway. Nothing retries it. The tell is a marker row left attached to the document
(APP_SEQUENCE_COUNTER_PENDING) that should have been deleted when the number arrived. Only a
BigLedger platform administrator can repair it, with an endpoint that fills in the tenant number on
any non-draft document missing one. You cannot fix this from a screen, and re-saving the document
will not trigger it.
“The company or branch number is blank on half my rows”
Expected, not broken. Only server_doc_1 exists on a draft. The company, branch, location and
device numbers are stamped at FINAL. If the blank rows are all drafts, there is nothing to fix; if a
FINAL row is blank, it is the case above.
“Two documents have the same number”
First, check which column and which document type you are looking at, because most apparent
duplicates are not duplicates. Each server_doc_ column is its own sequence per document type:
a sales invoice numbered 1000583 and a purchase invoice numbered 1000583 are unrelated and both
correct. server_doc_2 repeats across companies, server_doc_3 across branches, server_doc_4
across devices. Two branches each having a document numbered 41 is the feature working.
Second — and this is the part no screen tells you — the database enforces none of this. There is no unique constraint on any of the ten columns, at any scope. Every one of them has a plain index for searching and nothing more. Uniqueness within a document type is a convention the running-number generator keeps, not a rule the platform can enforce, and in the typical tenant it holds: grouping by document type and tenant number, the median tenant has zero duplicated groups. A minority do not, with tens of thousands of duplicated pairs.
Two ways a genuine duplicate gets in:
- A caller supplies its own number. The generator only fills a document whose
server_doc_1is empty, and nothing validates the field on create — so an integration that sends its own value keeps it, and no counter ever knew about it. If your tenant has duplicate tenant numbers, this is where to look first. - An import runs twice. Nothing enforces uniqueness on
client_doc_1toclient_doc_5either, so an integration that re-sends a batch without an idempotency key of its own produces two BigLedger documents carrying the same foreign number, both real, both postable. The duplicate is visible only when somebody searches for that number and gets two hits.
“Our two systems disagree about this document”
Three things can be true at once and they are often confused: the document arrived, the document posted, and the document was submitted. Arriving is not posting — an imported document lands as a draft unless the payload says otherwise, and a draft posts no journal and never reaches the e-invoice queue. Posting is not submitting. When a document is missing from a report, work in that order: is it in the table at all, is its posting status FINAL, and did the queue behind the report run.
“I searched for my own reference and found nothing”
The listing search box matches a fixed set of columns — by default server_doc_1, all five client
columns, doc_reference and the e-invoice number. If your reference lives somewhere else, for
instance in remarks or in a custom field, the search will never find it however exactly you type it.
The set of columns searched is configurable per applet.
What we cannot tell you
- What
server_doc_5is for. Declared, indexed, given a column comment, and written by no code path we can find — with two rows in the whole fleet to show for it. See above. - Which meaning of
server_doc_3is canonical — branch, as every applet path writes it, or company-and-location, as the older header-only-create path does. Recorded as an open question; do not build a report that depends on it without checking your own data first. - What
client_doc_2toclient_doc_5mean in your tenant. The platform assigns them no meaning, yet they are populated in around 40 of 90 tenants. Whatever they hold was decided by whoever built your integration, and the only reliable way to find out is to ask them or to look. - Whether the three tenants using
server_doc_4are using it as a device counter or have had an integration repurpose the slot. We can see the column is full; we cannot see why. - Whether a particular number is the one printed on the paper your customer is holding. That is a printable-format setting per branch, and this page cannot see your settings.
- The full list of document types. Four lists exist inside the product and they do not agree — 97, 79, 68 and 51 entries. The authority is your own tenant’s data, not a list we could keep current.
What success looks like
Open any document listing — a cash bill, a purchase invoice, whichever you use most — and do three things in under a minute.
- Turn on the company and branch number columns if they are not already showing. Every FINAL row now has three numbers and every draft has exactly one. That is the assignment rule, visible.
- Take the tenant number from the top FINAL row and paste it into the search box. You should get one result. Now paste the branch number instead: you may get several, one per branch. That is the scoping rule, visible — and if the tenant number returns two rows of the same document type, read “Two documents have the same number” above, because that is worth chasing.
- Open one document and look for a client document number. If it has one, that document came from
somewhere else, and the value in
client_sourcetells you where. If none of your documents has one, nobody is feeding you documents, and the whole client family is simply unused in your tenant — which is also a useful thing to know.
Where this sits in the rest of BigLedger
What must exist before any of this works. Nothing you have to set up. Counters create themselves the first time a document type is numbered, and no screen lists or edits them. The one thing worth doing before your first document is choosing a starting number, because it cannot be changed cleanly afterwards — Document Numbering step 3.
What this makes possible. Because every document is the same shape, features are written once against the shape rather than per document type: knock-off between an order and its invoice, contra between an invoice and a credit note, document links, approval routes, custom processors, and the import layer that lets another system feed you documents at all.
Its sibling, and when to use which. bl_fi_generic_doc_hdr holds your documents. The external
family (bl_fi_xtn_generic_doc_hdr) holds a copy of somebody else’s document that you are matching
against yours — it posts nothing and has no posting status. A document you imported is still your
document and belongs in the first family; it is marked with client_source, not moved elsewhere.
What breaks if the numbering is wrong. It is cross-module by construction. A blank tenant number
breaks the listing search, the e-invoice duplicate check that keys on server_doc_1 and the document
type, any integration matching on document number, and the reconciliation between your two systems —
all at once, and none of them says why.
What it pairs with in practice. Printable formats, because the number is only useful once it is printed somewhere a human can read; and the e-invoice configuration, because that is where a document number leaves your tenant and becomes somebody else’s reference.
Related documentation
- Document Numbering — read this next. The counter table behind every running number, the pool that keeps two tills from queueing, and what you can change.
- Where this number came from — stored figures against live ones, including the cost written back onto a document line.
- Printable Formats and Per-Branch Defaults — which number ends up on the paper.
- My E-Invoice Admin — how the number sent to LHDN
is built, and the duplicate check that keys on
server_doc_1. - AP Automation — the clearest live example of a workflow giving the client columns a fixed meaning of its own.