Skip to content
Help center

e-conomic Ledger

Every posted entry and both legs of every draft voucher, with account name, type and chart section.

What it answers

  • What was posted to an account in a period, and from which voucher?
  • Which entries are still drafts?

The view is named after the company, such as "Acme ApS Ledger", and reads the company's e-conomic data.

How to add it

Gatlas adds it for a company when the company connects e-conomic, and it fills in after the first sync. For a company that connected earlier, ask an AI assistant on a Read/write MCP connection:

Add the e-conomic Ledger view for Acme ApS.

The assistant creates it with the template id economic/ledger, after checking that the company's data has every column the template reads.

Columns

  • posting_status: posted (booked) or draft (in a journal, not booked yet).
  • leg: account, or contra for the opposite leg of a draft voucher. Posted entries are always account.
  • connection_label: The e-conomic agreement the row comes from; a company can hold several.
  • accounting_year: e-conomic accounting year of a posted entry; null on drafts.
  • date: Posting date.
  • voucher_number: Voucher number.
  • entry_number: Entry number of a posted entry; null on drafts.
  • entry_type: e-conomic entry type, e.g. financeVoucher, customerInvoice, supplierInvoice, openingEntry.
  • is_opening: True on openingEntry and transferredOpeningEntry rows, which restate a balance account at the start of a year.
  • account_number: Account the amount posts to.
  • account_name: Account name from the chart.
  • account_type: Account type from the chart: profitAndLoss or status for posting accounts.
  • chart_section: Nearest heading at or above the account in the company's chart.
  • counter_account_number: On a draft leg, the account on the voucher's other leg.
  • narrative: Entry text.
  • amount: Amount in the entry's currency, signed: positive is debit.
  • amount_base: Amount in the company's base currency, signed: positive is debit.
  • currency: Currency of amount.
  • vat_code: VAT code on this leg, if any.
  • department_number: Department, if the entry carries one.
  • journal_number: Journal a draft sits in; null on posted entries.
  • journal_name: Name of that journal.
  • supplier_number: Supplier on a draft voucher line, if any.
  • customer_number: Customer on a draft voucher line, if any.
  • supplier_invoice_number: Supplier invoice number on a draft, if any.
  • data_as_of: When this row was last synced.

What the AI assistant is told

The company's general ledger from e-conomic: one row per posted entry, and one row per leg of each draft voucher still in a journal.

posting_status = 'posted' is the booked ledger; 'draft' rows are not booked yet. For booked figures filter posting_status = 'posted'.

A draft voucher is a double entry: leg 'account' carries amount on account_number, leg 'contra' carries the opposite amount on the contra account. Both legs are here, so drafts sum correctly per account (cash included). A draft that is already posted is left out.

On a supplier or customer draft the account leg has no account_number: it is the supplier's or customer's balance (supplier_number, customer_number), which e-conomic books to the group's control account. A draft leg with a VAT code carries the gross amount; e-conomic splits the VAT out when it books. A draft line entered with only one side (often just the bank) has one leg, so its voucher does not balance until it is completed in e-conomic.

amount_base is in the company's base currency; amount is in the entry's currency. Sum amount_base, not amount, across currencies.

NEVER sum a balance-sheet (account_type 'status') account across accounting years: is_opening rows restate the cumulative balance at the start of every year, so a closing balance is the sum within ONE accounting_year (drafts carry no accounting_year; place them by date). P&L accounts carry no opening rows.

Monthly balances per account are ready-made in the Monthly Trial Balance view.

The SQL

__SCHEMA__ stands for the company's schema. To adapt the view, copy the SQL, put the schema in its place and change what your company needs.

WITH accts AS (
  SELECT
    COALESCE(a."_gatlas_connection_label", '') AS label_key,
    a."accountNumber" AS account_number,
    a."name" AS account_name,
    a."accountType" AS account_type,
    (SELECT h."name"
       FROM __SCHEMA__.economic_accounts h
      WHERE COALESCE(h."_gatlas_connection_label", '') = COALESCE(a."_gatlas_connection_label", '')
        AND h."accountType" IN ('heading', 'headingStart')
        AND h."accountNumber" <= a."accountNumber"
      ORDER BY h."accountNumber" DESC
      LIMIT 1) AS chart_section
  FROM __SCHEMA__.economic_accounts a
),
posted AS (
  SELECT
    'posted'::text AS posting_status,
    'account'::text AS leg,
    e."_gatlas_connection_label" AS connection_label,
    e."accountingYear" AS accounting_year,
    e."date"::date AS date,
    e."voucherNumber" AS voucher_number,
    e."entryNumber" AS entry_number,
    e."entryType" AS entry_type,
    e."accountNumber" AS account_number,
    e."accountName" AS source_account_name,
    NULL::bigint AS counter_account_number,
    e."text" AS narrative,
    e."amount" AS amount,
    e."amountInBaseCurrency" AS amount_base,
    e."currency" AS currency,
    NULLIF(btrim(e."vatCode"), '') AS vat_code,
    e."departmentNumber" AS department_number,
    NULL::bigint AS journal_number,
    NULL::text AS journal_name,
    NULL::bigint AS supplier_number,
    NULL::bigint AS customer_number,
    NULL::text AS supplier_invoice_number,
    e."_gatlas_synced_at" AS data_as_of
  FROM __SCHEMA__.economic_accounting_year_entries e
),
vouchers AS (
  SELECT
    v."_gatlas_connection_label" AS connection_label,
    COALESCE(v."_gatlas_connection_label", '') AS label_key,
    v."date"::date AS date,
    v."voucherNumber" AS voucher_number,
    v."entryType" AS entry_type,
    v."accountNumber" AS account_number,
    v."accountName" AS account_name,
    v."contraAccountNumber" AS contra_account_number,
    v."text" AS narrative,
    v."amount" AS amount,
    v."amountInBaseCurrency" AS amount_base,
    v."currency" AS currency,
    NULLIF(btrim(v."vatCode"), '') AS vat_code,
    NULLIF(btrim(v."contraVatCode"), '') AS contra_vat_code,
    v."departmentNumber" AS department_number,
    v."journalNumber" AS journal_number,
    v."journalName" AS journal_name,
    v."supplierNumber" AS supplier_number,
    v."customerNumber" AS customer_number,
    v."supplierInvoiceNumber" AS supplier_invoice_number,
    v."_gatlas_synced_at" AS data_as_of
  FROM __SCHEMA__.economic_journal_vouchers v
),
-- Posted rows that share a voucher number with a draft, built once so the
-- exclusions below are hash anti-joins rather than a scan per draft.
posted_keys AS (
  SELECT DISTINCT COALESCE(p.connection_label, '') AS label_key, p.voucher_number,
         p.date, p.account_number, p.amount
  FROM posted p
  WHERE p.voucher_number IN (SELECT d.voucher_number FROM vouchers d)
),
-- A draft that is already posted stays in the journal until the next sync.
-- The whole voucher goes when either of its legs is found posted.
open_vouchers AS (
  SELECT d.*
  FROM vouchers d
  WHERE NOT EXISTS (
          SELECT 1 FROM posted_keys k
          WHERE k.label_key = d.label_key AND k.voucher_number = d.voucher_number
            AND k.date = d.date AND k.account_number = d.account_number
            AND k.amount = d.amount)
    AND NOT EXISTS (
          SELECT 1 FROM posted_keys k
          WHERE k.label_key = d.label_key AND k.voucher_number = d.voucher_number
            AND k.date = d.date AND k.account_number = d.contra_account_number
            AND k.amount = -d.amount)
),
draft_legs AS (
  SELECT 'draft'::text AS posting_status, 'account'::text AS leg,
    o.connection_label, NULL::text AS accounting_year, o.date, o.voucher_number,
    NULL::bigint AS entry_number, o.entry_type, o.account_number,
    o.account_name AS source_account_name, o.contra_account_number AS counter_account_number,
    o.narrative, o.amount, o.amount_base, o.currency, o.vat_code, o.department_number,
    o.journal_number, o.journal_name, o.supplier_number, o.customer_number,
    o.supplier_invoice_number, o.data_as_of
  FROM open_vouchers o
  -- A line's account side is a ledger account, or a supplier or customer
  -- (account_number null) whose control account e-conomic picks when it
  -- books. A line with neither has only its contra side: its counterpart is
  -- another line of the same voucher.
  WHERE o.account_number IS NOT NULL
     OR o.supplier_number IS NOT NULL
     OR o.customer_number IS NOT NULL
  UNION ALL
  SELECT 'draft'::text, 'contra'::text,
    o.connection_label, NULL::text, o.date, o.voucher_number,
    NULL::bigint, o.entry_type, o.contra_account_number,
    NULL::text, o.account_number,
    o.narrative, -o.amount, -o.amount_base, o.currency, o.contra_vat_code, o.department_number,
    o.journal_number, o.journal_name, o.supplier_number, o.customer_number,
    o.supplier_invoice_number, o.data_as_of
  FROM open_vouchers o
  WHERE o.contra_account_number IS NOT NULL
),
ledger AS (
  SELECT * FROM posted
  UNION ALL
  SELECT * FROM draft_legs
)
SELECT
  l.posting_status,
  l.leg,
  l.connection_label,
  l.accounting_year,
  l.date,
  l.voucher_number,
  l.entry_number,
  l.entry_type,
  l.entry_type IN ('openingEntry', 'transferredOpeningEntry') AS is_opening,
  l.account_number,
  COALESCE(ac.account_name, l.source_account_name) AS account_name,
  ac.account_type,
  ac.chart_section,
  l.counter_account_number,
  l.narrative,
  l.amount,
  l.amount_base,
  l.currency,
  l.vat_code,
  l.department_number,
  l.journal_number,
  l.journal_name,
  l.supplier_number,
  l.customer_number,
  l.supplier_invoice_number,
  l.data_as_of
FROM ledger l
LEFT JOIN accts ac
  ON ac.label_key = COALESCE(l.connection_label, '')
 AND ac.account_number = l.account_number