Plank help · updated 2026-07-31

Reconciliation acts in 1C

How to build an «Акт сверки взаиморасчетов» in 1C without asserting things you do not know: whose figures go in which column, the contract rule, debit/credit direction, opening balance versus discrepancy, one currency, and verifying the amounts you wrote.

Agents: fetch the raw markdown of this page at /en/help/1c-reconciliation-acts.md

Reconciliation acts in 1C

An «Акт сверки взаиморасчетов» states, for one counterparty and one period, what each side thinks the other owes. It has two tabular sections because it holds two opinions — and the second one is not yours to write.

Everything on this page exists because a wrong act is not obviously wrong. It reads perfectly, it gets signed, and it goes to a counterparty.

There is a kit for this: install 1c-reconciliation-act-kz (see the 1C starter kit) rather than writing a fresh script. The rules below are what it enforces, and what you have to honour if you adapt it.

The counterparty's side is theirs, and you do not know it

ПоДаннымОрганизации is your books. ПоДаннымКонтрагента is theirs — and nothing in a script knows what theirs say.

There are three honest states, and the default is the third:

  1. You have their act. Fill the second table from it and report the discrepancies explicitly. That is the entire point of the document.
  2. A person has established they recorded the same operations. Mirror the rows — same dates, same documents, same types, same contracts, with debit and credit swapped — and mark the act agreed. create-act.py --counterparty-confirmed.
  3. You know nothing. Leave their table empty and «Сверка согласована» false.

Do not mirror by default. An act whose counterparty column was derived from your own books, stamped «Сверка согласована», asserts that they confirmed figures they never saw — on a document you are about to send them. The kit refuses, and warns when you write an unagreed act, so the empty column is a decision rather than an oversight.

The contract rule

Count the counterparty's contracts in Catalog_ДоговорыКонтрагентов, filtered by Owner_Key, the right Организация_Key, DeletionMark=false and IsFolder=false. Then:

Contracts foundWhat to do
noneleave the contract empty
exactly oneput it in the header and in the rows of both tables
more than oneleave the header empty — do not pick one

Picking one of several at random is worse than leaving it blank: a blank field reads as "across all contracts", which is true, while a guessed one asserts something about which debt is being reconciled.

Debit and credit direction

For customer settlements, in ПоДаннымОрганизации:

  • a sale (РеализацияТоваровУслуг) is a debit;
  • an incoming payment (ПлатежноеПоручениеВходящее, ПлатежныйОрдерПоступлениеДенежныхСредств) is a credit.

Do not take this on faith across configurations — and do not read the acts by hand either. Run:

python3 scripts/1c/reconciliation-act/check-direction.py
python3 scripts/1c/reconciliation-act/check-direction.py --since 2025-01-01

It reports which side each settlement type lands on in this base's own posted acts, with row counts, act counts, counterparty counts and date ranges, and prints the document_sides block to paste — or says why the evidence is not conclusive.

It needs at least 3 rows across at least 2 acts per document type. Below that it reports "not enough to call the convention" and prints nothing to paste. One row is an anecdote, and what gets pasted here signs every future act balance: three rows of one act can be the same operation repeated, and two acts of one row each is a coincidence away from being two documents of the same kind. Acts the kit wrote are excluded, because taking our own output as proof of the convention is how a first wrong guess becomes permanent.

Two things make it local rather than universal, and both surface as "BOTH — mixed evidence": the direction inverts by settlement kind (a customer receivable rises with a sale, a supplier payable falls with one), and it changes over time — a base's acts from three years ago can use the reverse of this year's. --since is how you ask what the base does now. The direction is the one thing here that a wrong answer makes silently, plausibly wrong.

Opening balance and discrepancy are different numbers

ОстатокНаНачало is the net of all settlement documents dated before the period — not zero, not the first row. Compute it from the same documents you filter for the body, using the same rules.

Расхождение is the gap between the two sides: your closing balance minus theirs. It is NOT your closing balance. Writing the closing balance there produces an act that declares a disagreement equal to the whole outstanding amount while its two tables agree by construction — and, if you also stamped «Сверка согласована», one that contradicts itself in two fields at once. With their table empty the discrepancy is your whole closing balance, which is the correct reading of "we have no figures from them".

Only posted, not-marked-for-deletion documents count. An unposted draft has moved no registers, so including it states a balance the accounting does not have. Count the ones you excluded and say how many: it is usually the answer to "why is this total not what I expected".

Every settlement type in the base, not only the ones you configured

The types you read must be discovered from the base — from what the deployment's entity_sets knows about — and then checked against document_sides. Doing it the other way round is a guard that cannot fire: if you only read the types you already declared, a type you forgot is never read, never reported, and silently missing from the balance — and the act reads as complete, with exit code 0 and nothing in the report to suggest a number is missing.

A type present in the base with no declared direction stops the run. So does a type that could not be read — an unreadable table is not an empty one.

One currency

Do not sum documents in different currencies into one balance, and do not take the currency from the template act. Take it from the documents; refuse when they disagree. A number that is right in one currency and labelled with another is wrong in the way that is hardest to see.

The contract on a row is that document's contract

The header contract follows the rule above. Each row carries its own document's ДоговорКонтрагента_Key, falling back to the header's only when the document names none. Relabelling a historical document with the counterparty's currently active contract misstates which debt is being reconciled.

Fields the kit sets for you

  • Posted = false, DeletionMark = false — a draft like everything else this kit writes. Posting stays the accountant's decision, made in 1C.
  • СверкаСогласованаfollows the confirmation, not the clock. See above.
  • Author, responsible and СписокСчетов come from a recent correct act of the same organisation in the same base. They are per-base settings, and a Ref_Key from another base means nothing. Currency does not — that comes from the documents.

Both tabular sections travel inside the parent document's own POST — rows cannot be created through their own entity set.

Duplicate guard

Before creating, look for an active act with the same organisation, the same counterparty and the same period. One period reconciled twice is two documents that disagree with each other by construction.

Resolve the counterparty by exact БИН/ИИН. Zero matches or several matches is stop-and-ask, never a guess by name.

Verify by reading — the amounts

After the write, re-read the document by Ref_Key and compare against what you computed: the period, the counterparty, the contract, the currency, the opening balance, the discrepancy, Posted=false, «Сверка согласована», and for both tables the row count and each row's «Дебет», «Кредит» and document reference.

Checking flags, dates and row counts is not verification. A configuration that recomputes or zeroes every monetary cell passes all of those — and the report then prints the numbers you calculated as though 1C had returned them. Build the report from what was stored, not from what was computed.

A 2xx is not evidence. See Working with 1C over OData §9.

Known limits

  • The duplicate check is not atomic. It lists existing acts, then POSTs. 1C's OData offers no uniqueness constraint to lean on, so two concurrent runs for the same counterparty and period can both pass and both write. Run one at a time.
  • No closing-balance field is written. Which attribute a configuration keeps it in is not established, and writing a field name nobody verified is worse than leaving the configuration to compute it from the rows.

Posting

Only on a separate, explicit instruction from the user.