Skip to content
The Generic Document

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_typeINTERNAL_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.

ColumnWhat it countsLabel you seeWhen it appearsHow much it is used
server_doc_1one sequence per document type, for the whole tenantDoc No (Tenant)the moment the document is created, draft or notevery document, in 74 of 90 tenants
server_doc_2the same, per companyDoc No (Company)only when the document is finalised73 of 90 tenants
server_doc_3the same, per branchDoc No (Branch)only when the document is finalised73 of 90 tenants
server_doc_locationthe same, per company and locationno label; never shownonly when the document is finalisednot measured; nothing reads it outward
server_doc_4the same, per point-of-sale deviceno label; never shownat finalise, and only if the document carries a device3 tenants, heavily
server_doc_5not establishedno label; never shownnever, as far as we can find2 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_1 from client in case of peer to peer data sync" — the column comment on client_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 Which figures BigLedger stores, and which it works out fresh.

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 has its own page: the margin report and the profit and loss don’t agree.

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_1 is 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_1 to client_doc_5 either, 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_5 is 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_3 is 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_2 to client_doc_5 mean 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_4 are 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.

  1. 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.
  2. 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.
  3. 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_source tells 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.
  • When two numbers don’t tally — stored figures against live ones, including the cost written back onto a document line: five symptoms, each with its check.
  • 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.
Last updated on