ZuloOne
English
Sign in
Menu

⚖️ Ukraine

Updated

The LocalizationUkraine model, Customization layer. Sales, Tax and Purchasing know nothing about Ukraine: the package sits on registers of its own and on extensions of other people's documents.

← Localizations · User: Ukrainian taxes · Analyst: the package scope · M.E.Doc · PrivatBank · Diia

Pages#

  • VAT (ПДВ) and the regimes — why the tax is computed by a totals driver and not by the document; why a single-tax payer (спрощенець) exemption is set through TaxMapping and not by an if (regime) branch inside Tax; the personal income tax (ПДФО) and military levy (військовий збір) withholdings from payroll and the PayrollLiability.Withheld resource through which a withholder declares itself.
  • Unified Register of Tax Invoices (ЄРПН) — the TaxDocument envelope through an operator, the submission queue and a separate channel for the question about the receipt; why a successful send does NOT mean registration, and why "sent, no answer" is resolved by a query and not by a second submission. There is no cloud M.E.Doc sandbox.
  • PrivatBank statement — Autoclient v3 GET, channel pb-statements on UaBankConnection, register UaBankStatement. Payments are not created from the statement. There is no Autoclient sandbox; Open Banking is a different API.
  • Diia — login is not in the country package; there is no stub channel (no agreement with the Ministry of Digital Transformation). Diia does not replace the tax invoice.

What is worth knowing before reading the code#

Three things in this package are arranged differently from what intuition suggests:

  • The tax is not accrued by the document. The liability is born of the first event — the shipment or the money (ПКУ 187.1) — and the documents only report the event into a register. That is why the accrual lives in a totals driver and not in a posting entry.
  • The regime is a property of the legal entity, not of the system. A limited liability company (ТОВ) on VAT (ПДВ) and several sole proprietors (ФОП) on single tax (єдиний податок) are accounted for in one database. The UaTaxRegime field sits on the LegalEntity extension, does not reach the generated class, is read out of the bag and written as the enum's number.
  • Rates and codes are data, not constants. The military levy (ВЗ) rate has already changed (1.5% → 5% from 2024-12-01), the income limit changes every year along with the minimum wage, and the State Tax Service (ДПС) form number depends on the version of the form. None of those numbers is hardcoded into a script.

Amount in words#

IUaAmountInWords.Hryvnias(decimal) writes a hryvnia amount the way a payment invoice and a reconciliation act print it. Use it instead of writing your own:

Amount Text
1 Одна гривня 00 копійок
21.01 Двадцять одна гривня 01 копійка
1 234.56 Одна тисяча двісті тридцять чотири гривні 56 копійок
3 022 000.40 Три мільйони двадцять дві тисячі гривень 40 копійок

The rules it follows:

  • Hryvnia and thousand are feminine (одна, дві). Million and billion are masculine (один, два).
  • The noun form follows the number: 1 takes one form, 2–4 another, the rest a third. 11–14 always take the third form.
  • Kopecks are printed as two digits.
  • The amount is first rounded to the kopeck, half away from zero.

The service is a plain function: it reads no data and writes none. The test is UaAmountInWordsTest. The Okrasheno client model uses it on its payment invoice for a sales order (OkrashenoSalesOrderPaymentInvoiceZr).

Same topic for other readers: Analyst

Where next#

Was this page helpful?