PHPackages                             jeancz/czech-vat - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. jeancz/czech-vat

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

jeancz/czech-vat
================

Czech VAT Return (DPHDP3) and VAT Control Statement (DPHKH1) XML generator

v1.0.1(1mo ago)1770↑41.7%1MITPHPPHP ^8.4CI passing

Since Apr 24Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/jeancz/czech-vat)[ Packagist](https://packagist.org/packages/jeancz/czech-vat)[ RSS](/packages/jeancz-czech-vat/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (2)Dependencies (8)Versions (5)Used By (0)

jeancz/czech-vat
================

[](#jeanczczech-vat)

PHP knihovna pro generování XML souborů daňového přiznání k DPH (**DPHDP3**) a kontrolního hlášení DPH (**DPHKH1**) ve formátu požadovaném portálem EPO Finanční správy ČR.

Obsah
-----

[](#obsah)

- [Požadavky](#po%C5%BEadavky)
- [Instalace](#instalace)
- [Rychlý start](#rychl%C3%BD-start)
- [Sazby DPH](#sazby-dph)
- [Faktury a řádky](#faktury-a-%C5%99%C3%A1dky)
- [Zdaňovací období](#zda%C5%88ovac%C3%AD-obdob%C3%AD)
- [Plátce](#pl%C3%A1tce)
- [Generování XML](#generov%C3%A1n%C3%AD-xml)
    - [Kontrolní hlášení](#kontroln%C3%AD-hl%C3%A1%C5%A1en%C3%AD)
    - [Daňové přiznání k DPH](#da%C5%88ov%C3%A9-p%C5%99izn%C3%A1n%C3%AD-k-dph)
    - [Typy podání](#typy-pod%C3%A1n%C3%AD)
- [XSD validace](#xsd-validace)
- [Výjimky](#v%C3%BDjimky)
- [Architektura](#architektura)
- [Rozšíření](#roz%C5%A1%C3%AD%C5%99en%C3%AD)

---

Požadavky
---------

[](#požadavky)

- PHP 8.4+
- Rozšíření `ext-dom` a `ext-libxml` (součást standardní instalace PHP)
- Žádné další závislosti

Instalace
---------

[](#instalace)

```
composer require jean/czech-vat
```

---

Rychlý start
------------

[](#rychlý-start)

```
use JeanCz\CzechVat\VatFilingGenerator;
use JeanCz\CzechVat\Enum\VatRateType;
use JeanCz\CzechVat\Model\Invoice\Invoice;
use JeanCz\CzechVat\Model\Invoice\InvoiceLine;
use JeanCz\CzechVat\Model\Invoice\InvoiceCollection;
use JeanCz\CzechVat\Model\Period\TaxPeriod;
use JeanCz\CzechVat\Model\Taxpayer\Taxpayer;
use JeanCz\CzechVat\Model\VatRates\VatRates;

// 1. Sazebník platný pro dané období
$rates = VatRates::current(); // od 1.1.2024: základní 21 %, snížená 12 %

// 2. Plátce
$taxpayer = Taxpayer::legalEntity(
    vatId:        'CZ12345678',
    taxOfficeCode: '452',
    companyName:  'Moje Firma s.r.o.',
    street:       'Hlavní',
    houseNumber:  '1',
    city:         'Praha',
    postalCode:   '11000',
    email:        'ucetni@mojefirma.cz',
);

// 3. Zdaňovací období
$period = TaxPeriod::monthly(2025, 1);

// 4. Faktury
$invoices = (new InvoiceCollection())
    ->addIssued(
        // Vydaná faktura nad 10 000 Kč — vykazuje se jednotlivě v KH
        new Invoice(
            lines: [
                new InvoiceLine(VatRateType::Standard, taxBase: 100_000, vat: 21_000, rates: $rates),
            ],
            partnerVatId:  'CZ87654321',
            documentNumber: 'FAK-2025-001',
            taxPointDate:  new DateTimeImmutable('2025-01-15'),
        ),
        // Vydaná faktura do 10 000 Kč — souhrnně v sekci A.5 KH
        new Invoice([
            new InvoiceLine(VatRateType::Standard, taxBase: 5_000, vat: 1_050, rates: $rates),
        ]),
    )
    ->addReceived(
        // Přijatá faktura nad 10 000 Kč — sekce B.2 KH
        new Invoice(
            lines: [
                new InvoiceLine(VatRateType::Standard, taxBase: 50_000, vat: 10_500, rates: $rates),
            ],
            partnerVatId:           'CZ11111111',
            documentNumber:         'MUJ-REF-001',
            supplierDocumentNumber: 'DOD-INV-999',
            taxPointDate:           new DateTimeImmutable('2025-01-10'),
        ),
    );

// 5. Generování
$generator = new VatFilingGenerator($taxpayer, $period, $invoices);

file_put_contents('kontrolni_hlaseni.xml', $generator->generateControlStatement());
file_put_contents('danove_priznani.xml',   $generator->generateVatReturn());
```

---

Sazby DPH
---------

[](#sazby-dph)

Sazby nejsou součástí enumu, ale konfigurovatelného objektu `VatRates`. Oddělení sémantiky od konkrétních procent zajišťuje, že při legislativní změně stačí aktualizovat sazebník — ne upravovat enum a s ním veškerý kód, který na něj závisí.

### Předdefinované sazebníky

[](#předdefinované-sazebníky)

```
use JeanCz\CzechVat\Model\VatRates\VatRates;

// Aktuální sazby (od 1. 1. 2024)
// Základní: 21 %, Snížená: 12 %, Nulová: 0 %
$rates = VatRates::current();

// Stejné, explicitnější název
$rates = VatRates::validFrom20240101();

// Sazby platné do 31. 12. 2023
// Základní: 21 %, Snížená: 15 %, Druhá snížená: 10 %, Nulová: 0 %
$rates = VatRates::validUntil20231231();
```

### Vlastní sazebník

[](#vlastní-sazebník)

```
use JeanCz\CzechVat\Enum\VatRateType;

$rates = VatRates::custom([
    VatRateType::Standard->value => 21,
    VatRateType::Reduced->value  => 12,
    VatRateType::Zero->value     => 0,
]);
```

### Práce se sazebníkem

[](#práce-se-sazebníkem)

```
// Zjistit procento pro daný typ
$rates->percentage(VatRateType::Standard); // → 21

// Přeložit konkrétní procento na sémantický typ
$rates->resolve(12); // → VatRateType::Reduced

// Ověřit, zda je sazba registrována
$rates->has(12);                          // → true
$rates->hasType(VatRateType::SecondReduced); // → false (v aktuálním sazebníku)
```

### Typy sazeb (`VatRateType`)

[](#typy-sazeb-vatratetype)

CasePopisEPO XML atribut`VatRateType::Standard`Základní sazba`zakl_dane1` / `dan1``VatRateType::Reduced`Snížená sazba`zakl_dane2` / `dan2``VatRateType::SecondReduced`Druhá snížená (do 31. 12. 2023)`zakl_dane3` / `dan3``VatRateType::Zero`Nulová sazbanevykazuje se v řádcích DPH---

Faktury a řádky
---------------

[](#faktury-a-řádky)

### `InvoiceLine` — jeden řádek faktury

[](#invoiceline--jeden-řádek-faktury)

```
use JeanCz\CzechVat\Model\Invoice\InvoiceLine;
use JeanCz\CzechVat\Enum\VatRateType;

$rates = VatRates::current();

// Explicitní základ daně a DPH
$line = new InvoiceLine(
    vatRateType: VatRateType::Standard,
    taxBase:     10_000.0,
    vat:         2_100.0,
    rates:       $rates,
);

// Automatický výpočet DPH ze základu
$line = InvoiceLine::fromTaxBase(
    vatRateType: VatRateType::Reduced,
    taxBase:     5_000.0,
    rates:       $rates,
);
// → vat = 600.00 (12 % ze 5 000)
```

Dobropis se vyjádří záporným základem daně i DPH:

```
$creditNote = new InvoiceLine(VatRateType::Standard, taxBase: -10_000.0, vat: -2_100.0, rates: $rates);
```

### `Invoice` — celá faktura

[](#invoice--celá-faktura)

Faktura přijímá kolekci řádků s (potenciálně různými) sazbami. Pokud celková částka včetně DPH překračuje **10 000 Kč**, jsou pole `partnerVatId`, `documentNumber` a `taxPointDate` povinná — jejich absence způsobí výjimku při konstrukci.

```
use JeanCz\CzechVat\Model\Invoice\Invoice;

// Faktura nad prahem — vykazuje se jednotlivě v kontrolním hlášení
$invoice = new Invoice(
    lines: [
        new InvoiceLine(VatRateType::Standard, taxBase: 80_000, vat: 16_800, rates: $rates),
        new InvoiceLine(VatRateType::Reduced,  taxBase: 20_000, vat:  2_400, rates: $rates),
    ],
    partnerVatId:           'CZ87654321',   // povinné nad 10 000 Kč
    documentNumber:         'FAK-2025-042', // povinné nad 10 000 Kč
    supplierDocumentNumber: 'DOD-789',      // číslo dodavatele (pro přijaté faktury)
    taxPointDate:           new DateTimeImmutable('2025-01-20'), // povinné nad 10 000 Kč
    isReverseCharge:        false,          // přenesení daňové povinnosti
);

// Faktura pod prahem — nevyžaduje DIČ partnera ani datum
$smallInvoice = new Invoice([
    new InvoiceLine(VatRateType::Standard, taxBase: 4_000, vat: 840, rates: $rates),
]);
```

### `InvoiceCollection` — kolekce faktur

[](#invoicecollection--kolekce-faktur)

```
use JeanCz\CzechVat\Model\Invoice\InvoiceCollection;

$invoices = (new InvoiceCollection())
    ->addIssued($invoice1, $invoice2, $invoice3)
    ->addReceived($received1, $received2);
```

Metody `addIssued()` a `addReceived()` jsou immutable — vracejí novou instanci.

---

Zdaňovací období
----------------

[](#zdaňovací-období)

```
use JeanCz\CzechVat\Model\Period\TaxPeriod;

// Měsíční plátce (nebo kontrolní hlášení — vždy měsíční)
$period = TaxPeriod::monthly(2025, 1);

// Čtvrtletní plátce (daňové přiznání)
$period = TaxPeriod::quarterly(2025, 1); // Q1

// Odvození z DateTimeInterface
$period = TaxPeriod::fromDate(new DateTimeImmutable('2025-03-15'));
// → monthly(2025, 3)

// Dotazování
$period->isMonthly();   // true / false
$period->isQuarterly(); // true / false
$period->startDate();   // DateTimeImmutable — první den období
$period->endDate();     // DateTimeImmutable — poslední den období
(string) $period;       // '2025-01' nebo '2025-Q1'
```

> **Poznámka:** Kontrolní hlášení se podává vždy za kalendářní měsíc, i pro čtvrtletní plátce DPH. Čtvrtletní plátce podá tři měsíční kontrolní hlášení a jedno čtvrtletní daňové přiznání.

---

Plátce
------

[](#plátce)

### Právnická osoba

[](#právnická-osoba)

```
use JeanCz\CzechVat\Model\Taxpayer\Taxpayer;
use JeanCz\CzechVat\Enum\VatPayerType;

$taxpayer = Taxpayer::legalEntity(
    vatId:                 'CZ12345678',
    taxOfficeCode:         '452',        // kód FÚ z číselníku UFO
    companyName:           'Moje Firma s.r.o.',
    street:                'Obchodní',
    houseNumber:           '15',
    city:                  'Brno',
    postalCode:            '60200',
    country:               'ČESKÁ REPUBLIKA', // výchozí hodnota
    vatPayerType:          VatPayerType::VatPayer, // výchozí hodnota
    email:                 'dph@mojefirma.cz',
    taxOfficeWorkplaceCode: '2401',      // územní pracoviště (volitelné)
);
```

### Fyzická osoba

[](#fyzická-osoba)

```
$taxpayer = Taxpayer::naturalPerson(
    vatId:                'CZ7001011234',
    taxOfficeCode:        '451',
    firstName:            'Jan',
    lastName:             'Novák',
    street:               'Lipová',
    houseNumber:          '3',
    city:                 'Praha',
    postalCode:           '13000',
    title:                'Ing.',          // titul (volitelné)
    phone:                '123456789',     // telefon (volitelné, max 14 znaků)
    orientationNumber:    '5a',            // číslo orientační (volitelné, max 4 znaky)
    mainEconomicActivity: 6201,            // CZ-NACE kód hlavní ekonomické činnosti (volitelné)
);
```

Přehled polí specifických pro fyzické osoby:

ParametrXML atributUmístěníPopis`title``titul`VetaP (KH i DP3)Akademický nebo profesní titul`phone``c_telef`VetaP (KH i DP3)Kontaktní telefon (max 14 znaků)`orientationNumber``c_orient`VetaP (KH i DP3)Číslo orientační adresy (max 4 znaky)`mainEconomicActivity``c_okec`VetaD (pouze DP3)Kód CZ-NACE převažující ekonomické činnosti### Typy plátce (`VatPayerType`)

[](#typy-plátce-vatpayertype)

Case§ZDPHPopis`VatPayer`§ 6–6faPlátce DPH (výchozí)`IdentifiedPerson`§ 6g–6lIdentifikovaná osoba`Group`§ 5aSkupina`NonPayer108`§ 108Neplátce s povinností přiznat daň`NonPayerAcquisition`§ 19cNeplátce — pořízení NDP`NonPayerDelivery`§ 19bNeplátce — dodání NDP---

Generování XML
--------------

[](#generování-xml)

Vstupním bodem je fasáda `VatFilingGenerator`:

```
use JeanCz\CzechVat\VatFilingGenerator;

$generator = new VatFilingGenerator(
    taxpayer:        $taxpayer,
    period:          $period,
    invoices:        $invoices,
    softwareName:    'Moje Aplikace',   // volitelné, výchozí 'jeancz/czech-vat'
    softwareVersion: '1.0',             // volitelné
);
```

### Kontrolní hlášení

[](#kontrolní-hlášení)

```
$xml = $generator->generateControlStatement();

file_put_contents('kh_2025_01.xml', $xml);
```

Sekce KH, které builder plní automaticky:

SekceObsah`VetaA4`Vydaná zdanitelná plnění nad 10 000 Kč (jednotlivě)`VetaA5`Vydaná zdanitelná plnění do 10 000 Kč (souhrnně)`VetaA1`Vydaná plnění v režimu přenesení daňové povinnosti nad prahem`VetaB2`Přijatá zdanitelná plnění nad 10 000 Kč (jednotlivě)`VetaB3`Přijatá zdanitelná plnění do 10 000 Kč (souhrnně)`VetaB1`Přijatá plnění v režimu přenesení daňové povinnosti nad prahem`VetaC`Kontrolní součty (křížová kontrola s DP)### Daňové přiznání k DPH

[](#daňové-přiznání-k-dph)

```
$xml = $generator->generateVatReturn();

file_put_contents('dp_dph_2025_01.xml', $xml);
```

Sekce DP, které builder plní automaticky:

SekceŘádky DPObsah`Veta1`1, 2Základ daně a daň na výstupu`Veta4`40, 41, 46, 47Odpočet daně na vstupu`Veta6`62–65Výsledná daňová povinnost / nadměrný odpočet### Typy podání

[](#typy-podání)

Výchozí typ je vždy **řádné** podání. Ostatní typy se předají jako argument:

```
use JeanCz\CzechVat\Enum\VatReturnFilingType;
use JeanCz\CzechVat\Enum\ControlStatementFilingType;

// Opravné daňové přiznání (před uplynutím lhůty)
$xml = $generator->generateVatReturn(VatReturnFilingType::Corrective);

// Dodatečné daňové přiznání
$xml = $generator->generateVatReturn(VatReturnFilingType::Supplementary);

// Následné kontrolní hlášení (po zjištění chyby)
$xml = $generator->generateControlStatement(ControlStatementFilingType::Subsequent);

// Následné KH jako odpověď na výzvu správce daně
$xml = $generator->generateControlStatement(
    filingType:             ControlStatementFilingType::Subsequent,
    authorityRequestNumber: '12345678/25/4501-00000-123456',
);
```

#### Přehled typů podání

[](#přehled-typů-podání)

**Daňové přiznání (`VatReturnFilingType`)**

CaseKódPopis`Regular`BŘádné`Corrective`OOpravné (nahrazuje podané řádné)`Supplementary`DDodatečné`SupplementaryCorrective`EDodatečné/opravné**Kontrolní hlášení (`ControlStatementFilingType`)**

CaseKódPopis`Regular`BŘádné`RegularCorrective`OŘádné/opravné (před uplynutím lhůty)`Subsequent`NNásledné (po uplynutí lhůty)`SubsequentCorrective`ENásledné/opravné---

XSD validace
------------

[](#xsd-validace)

Balíček obsahuje přiložená XSD schémata z EPO portálu. Validaci lze volat samostatně kdykoli — například v testech nebo před odesláním.

```
use JeanCz\CzechVat\Generator\XsdValidator;
use JeanCz\CzechVat\Exception\XmlValidationException;

try {
    XsdValidator::forControlStatement()->validate($ksXml);
    XsdValidator::forVatReturn()->validate($dpXml);
} catch (XmlValidationException $e) {
    echo $e->getMessage();

    // Detailní seznam chyb z libxml
    foreach ($e->errors as $error) {
        echo $error . PHP_EOL;
    }
}
```

Validátor lze použít i s vlastním XSD souborem:

```
$validator = new XsdValidator('/cesta/k/memu/schematu.xsd');
$validator->validate($xml);
```

---

Výjimky
-------

[](#výjimky)

Všechny výjimky rozšiřují `JeanCz\CzechVat\Exception\CzechVatException` (která rozšiřuje `\RuntimeException`), takže je lze chytat hromadně nebo jednotlivě.

VýjimkaKdy se vyhodí`InvalidInvoiceException`Prázdný seznam řádků; záporný základ s kladným DPH; faktura nad 10 000 Kč bez povinných polí`InvalidVatRateException`Dotaz na typ sazby, který není v sazebníku; záporné procento v `VatRates::custom()``InvalidTaxpayerException`Chybný formát DIČ; chybějící jméno / název firmy`InvalidPeriodException`Měsíc mimo 1–12; čtvrtletí mimo 1–4`XmlGenerationException`Interní chyba DOM při sestavování XML`XmlValidationException`XML neodpovídá XSD schématu; obsahuje seznam chyb v `$e->errors`---

Architektura
------------

[](#architektura)

```
src/
├── VatFilingGenerator.php          ← fasáda, hlavní vstupní bod
│
├── Builder/
│   ├── AbstractXmlBuilder.php      ← sdílené DOM helpery, formátování
│   ├── ControlStatementBuilder.php ← sestavuje DPHKH1
│   └── VatReturnBuilder.php        ← sestavuje DPHDP3
│
├── Contract/
│   ├── XmlGeneratorInterface.php   ← generate(): string
│   └── XmlValidatorInterface.php   ← validate(string): void
│
├── Enum/
│   ├── VatRateType.php             ← Standard | Reduced | SecondReduced | Zero
│   ├── VatReturnFilingType.php     ← Regular | Corrective | Supplementary | …
│   ├── ControlStatementFilingType.php
│   ├── TaxpayerType.php            ← NaturalPerson | LegalEntity
│   └── VatPayerType.php            ← VatPayer | IdentifiedPerson | …
│
├── Exception/                      ← typovaná hierarchie výjimek
│
├── Generator/
│   └── XsdValidator.php            ← volitelná XSD validace přes libxml
│
└── Model/
    ├── Invoice/
    │   ├── InvoiceLine.php         ← jeden řádek: VatRateType + základ + DPH
    │   ├── Invoice.php             ← sada řádků + metadata + validace prahu
    │   └── InvoiceCollection.php   ← vydané + přijaté faktury, agregace
    ├── Period/
    │   └── TaxPeriod.php           ← měsíční nebo čtvrtletní období
    ├── Taxpayer/
    │   └── Taxpayer.php            ← plátce (FO / PO), named constructors
    └── VatRates/
        └── VatRates.php            ← konfigurovatelný sazebník DPH

```

**Klíčové principy:**

- **Žádné závislosti** — pouze standardní PHP s `ext-dom` a `ext-libxml`.
- **Immutable value objects** — `Invoice`, `InvoiceLine`, `TaxPeriod`, `Taxpayer` a `InvoiceCollection` jsou neměnné po vytvoření.
- **Validace v modelu** — `Invoice` vynucuje pravidla (práh 10 000 Kč, povinná pole) při konstrukci, ne až při generování XML.
- **Sazby oddělené od sémantiky** — `VatRateType` popisuje kategorii sazby, `VatRates` nese konkrétní procenta pro dané období.
- **EPO mapování inkapsulovano** — historické pojmenování atributů XSD (`zakl_dane1`, `obrat23`, `dan5`, …) je skryto uvnitř builderů a enumu `VatRateType::epoAttributeIndex()`.

---

Rozšíření
---------

[](#rozšíření)

### Vlastní builder

[](#vlastní-builder)

Implementujte `XmlGeneratorInterface` a předejte výsledné XML přímo do `XsdValidator`:

```
use JeanCz\CzechVat\Contract\XmlGeneratorInterface;

final class MyCustomVatReturnBuilder implements XmlGeneratorInterface
{
    public function generate(): string
    {
        // vlastní implementace
    }
}

$xml = (new MyCustomVatReturnBuilder(...))->generate();
XsdValidator::forVatReturn()->validate($xml);
```

### Vlastní validátor

[](#vlastní-validátor)

Implementujte `XmlValidatorInterface` pro vlastní validační logiku (např. odesílání na testovací endpoint EPO):

```
use JeanCz\CzechVat\Contract\XmlValidatorInterface;

final class EpoSandboxValidator implements XmlValidatorInterface
{
    public function validate(string $xml): void
    {
        // HTTP požadavek na testovací EPO endpoint
    }
}
```

---

Testování
---------

[](#testování)

```
composer install
./vendor/bin/phpunit
```

Testovací sada pokrývá unit testy modelů (`InvoiceLine`, `Invoice`, `TaxPeriod`, `VatRates`) i integrační testy s XSD validací obou výstupních formátů.

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance92

Actively maintained with recent releases

Popularity21

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 83.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~53 days

Total

2

Last Release

39d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3366805?v=4)[Jan Smrčka](/maintainers/jeancz)[@jeancz](https://github.com/jeancz)

---

Top Contributors

[![jeancz](https://avatars.githubusercontent.com/u/3366805?v=4)](https://github.com/jeancz "jeancz (5 commits)")[![baasha](https://avatars.githubusercontent.com/u/636120?v=4)](https://github.com/baasha "baasha (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/jeancz-czech-vat/health.svg)

```
[![Health](https://phpackages.com/badges/jeancz-czech-vat/health.svg)](https://phpackages.com/packages/jeancz-czech-vat)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
