PHPackages                             stboris/laravel-eracun - 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. stboris/laravel-eracun

ActiveLibrary

stboris/laravel-eracun
======================

Read, build and validate Croatian eRačun (HR CIUS / EXT 2025) UBL documents in PHP.

v0.1.0(today)00MITPHPPHP ^8.3CI passing

Since Jul 28Pushed todayCompare

[ Source](https://github.com/stboris/laravel-eracun)[ Packagist](https://packagist.org/packages/stboris/laravel-eracun)[ RSS](/packages/stboris-laravel-eracun/feed)WikiDiscussions main Synced today

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

laravel-eracun
==============

[](#laravel-eracun)

Read, build and validate Croatian **eRačun** documents — UBL 2.1 under HR CIUS / EXT 2025.

```
use Stboris\Eracun\Reading\UblReader;

$eracun = (new UblReader())->fromFile('racun.xml');

$eracun->number;                    // "1-P1-1"
$eracun->seller->name;              // "TVRTKA A d.o.o."
$eracun->seller->oib();             // "12345678901"
$eracun->operator->name;            // "Operater1"   (HR-BT-4, no EN 16931 equivalent)
$eracun->totals->payableAmount;     // Amount("125.00", "EUR")
$eracun->kpdCodes();                // ["62.20.20"]

$eracun->lines[0]->item->taxCategory->hrCategory;   // HrVatCategory::Pdv25
```

Typed objects throughout. No arrays of magic string keys.

The Croatian bits that are easy to get wrong
--------------------------------------------

[](#the-croatian-bits-that-are-easy-to-get-wrong)

**One HR marker fixes three codes.** `HR:POVNAK` is not just a label — it pins the UNTDID 5305 category to `E`, the extension category (HR-BT-18) to `O`, and the tax scheme to `OTH`. Getting them out of step is a rejection. The mapping is table HR-TB-2 of the specification, encoded once:

```
HrVatCategory::Povnak->vatCategory();     // VatCategory::Exempt   → BT-118
HrVatCategory::Povnak->hrCategoryCode();  // "O"                   → HR-BT-18
HrVatCategory::Povnak->taxScheme();       // "OTH"                 → UNTDID 5153
HrVatCategory::Povnak->isPassThrough();   // true — stays out of the VAT base
```

**Amounts keep their literal text.** `HR-BR-56` limits amounts to 30 characters and 10 decimal places, and it measures the written form — so `100.000000` and `100.00` are not interchangeable. Parsing to `float` on the way in destroys the only thing that rule can be checked against, and puts binary rounding error into the reconciliation rules.

```
$price = $eracun->lines[0]->priceAmount;

$price->value;      // "100.000000"  — exactly as written
$price->decimals(); // 6
$price->toFloat();  // 100.0         — for display, never for round-tripping
```

**The extension is a second tax tree.** HR CIUS is not only a CIUS. `ext-2025` adds `hrextac:HRFISK20Data` — a parallel tax-total structure carried in `ext:UBLExtensions`— required whenever amounts sit outside the ordinary VAT base, including every seller outside the VAT system.

```
$eracun->hasHrExtension();                  // true
$eracun->hrData->outOfScopeOfVatAmount;     // Amount("0.00", "EUR")
$eracun->hrData->taxSubtotals;              // list
```

Building
--------

[](#building)

```
use Stboris\Eracun\Writing\UblWriter;

$xml = (new UblWriter())->toXml($eracun);
```

Round-tripping is exact: read a document, write it again, and the result parses back to an equal object. Amounts are written from their literal strings, never reformatted, so `100.000000` does not silently become `100.00`.

Two things the writer takes care of that are easy to get wrong by hand:

**Nothing empty is ever emitted.** `HR-BR-33` forbids empty elements, so every value goes through a helper that writes nothing rather than ``, and containers that end up childless are removed. The one exception is the signature placeholder, which the rule explicitly exempts — pass `new UblWriter(includeSignaturePlaceholder: false)` to drop it.

**Element order follows the XSD, not the examples.** UBL types are sequences, so a field in the wrong position is invalid before any business rule is reached. Credit notes are not just invoices with a different element name: `CreditNoteType` has **no `cbc:DueDate`at all** and puts `cbc:TaxPointDate` *before* the type code. A credit note that needs a due date has to carry it in `cac:PaymentMeans/cbc:PaymentDueDate`.

Validating
----------

[](#validating)

```
use Stboris\Eracun\Validation\Validator;

$result = Validator::default()->validateFile('racun.xml');

$result->isValid();       // false
$result->brokenCodes();   // ["HR-BR-9", "HR-BR-40"]
$result->messages();      // ["[HR-BR-9] HR-BT-5: Račun mora sadržavati ispravan OIB operatera.", …]
```

Violations carry the **official** rule identifier, so a message from this package matches the code in a rejection report from a posrednik or from the Porezna uprava validator. Every message exists in Croatian and English.

### What this validator does and does not check

[](#what-this-validator-does-and-does-not-check)

It is a **business-rule validator, not a conformance validator**. Passing it does not guarantee a document will be accepted.

Rather than describing that in prose, the package reports it:

```
Validator::default()->coverage();
// ['ratio' => '62/62', 'implemented' => [...], 'missing' => []]
```

All 62 rules published in the official Schematron (version 2026-03-13) are implemented, and a test asserts that every code the package claims actually exists in that file, so the two cannot drift apart.

### Verified against the official Schematron

[](#verified-against-the-official-schematron)

The rules are reimplemented in PHP, so the question that matters is whether they agree with the artifact they were copied from. They do, and it is checkable:

```
tools/schematron-verify.sh          # runs the official .sch via Saxon in Docker
php tools/compare-with-schematron.php
# 20 agreed, 0 disagreed
```

Both the 20 reference invoices and the same documents after a round-trip through `UblWriter` produce byte-identical violation sets under the official Schematron and under this package. The harness is a verification tool, not a dependency — nothing in the package needs Java or Docker.

It is still not a *conformance* validator: the XSD structural layer is separate, and agreement on 40 documents is evidence rather than proof. One implementation detail is worth knowing:

- **`HR-BR-33`** (no empty XML elements) is about the serialised form and cannot be expressed against the object model. It runs under `validateXml()` and `validateFile()`, and appears in `skippedCodes` when validating an in-memory document.

### The official examples do not pass

[](#the-official-examples-do-not-pass)

Worth knowing before you use them as fixtures. All 20 reference invoices published by the Porezna uprava fail the current rule set, for reasons that are entirely historical:

RuleFilesWhy`HR-BR-40`20/20Every example is dated 2025; the rule requires issue dates from 2026-01-01`HR-BR-9`20/20The seller placeholder `12345678901` fails the OIB checksum`HR-BR-53`19/20Same placeholder as the seller tax identifier`HR-BR-25`1/20The leasing example (type 394) carries no KPD code and 394 is not exemptThe examples were published 12.12.2025; the Schematron was revised 13.03.2026. Do not write a test that asserts they are all clean.

Rendering
---------

[](#rendering)

```
use Stboris\Eracun\Rendering\EracunRenderer;

echo (new EracunRenderer())->render($eracun);          // Croatian
echo (new EracunRenderer())->render($eracun, 'en');    // English
```

A UBL invoice has no canonical visual form — the XML *is* the invoice and any rendering is a *vizualizacija*. The default template says so in its footer, which matters: a printed rendering is not the legal document.

The template is plain PHP, not Blade, so the core works outside Laravel. Point the renderer at your own file, or publish the shipped one and edit it.

PDF is an interface with one optional implementation, because a PDF engine is a heavy and opinionated dependency that does not belong in a library about XML:

```
composer require dompdf/dompdf
```

```
use Stboris\Eracun\Rendering\DompdfRenderer;

file_put_contents('racun.pdf', (new DompdfRenderer())->render($eracun));
```

Laravel
-------

[](#laravel)

Auto-discovered. Nothing to register.

```
$request->validate([
    'oib'   => ['required', new ValidOib()],
    'kpd'   => ['required', new ValidKpd()],
    'racun' => ['required', 'file', new ValidEracunXml()],
]);
```

`ValidEracunXml` checks only that the document is *readable* by default, because a received invoice that breaks a rule still has to be accepted, stored, and then refused through the odbijanje workflow. Pass `enforceRules: true` when validating something you are about to issue — you then get one message per violation, each carrying its official rule code.

```
php artisan vendor:publish --tag=eracun-config
php artisan vendor:publish --tag=eracun-views
php artisan vendor:publish --tag=eracun-lang
```

`illuminate/support` is a dev dependency, not a requirement: the core is plain PHP and installs anywhere.

Documentation
-------------

[](#documentation)

**[Field reference](docs/reference.md)** — every HR CIUS field, its business term, cardinality, and the PHP property that carries it. Including the HR-TB-2 marker table, which is the single most error-prone part of the specification.

Scope
-----

[](#scope)

This package is about the eRačun **document**, not about transmitting it.

In scope: reading, building, rendering, validating, and the code lists.

Out of scope, permanently: fiskalizacija to the Porezna uprava, transmission through an informacijski posrednik, B2C fiskalizacija, eIzvještavanje. Those need a certificate or a commercial contract.

`Transport` is provided as an interface with **no implementations, and there never will be any**. It exists so an adapter can be added without forking. Writing one against a posrednik API with *your own* credentials is ordinary integration work and needs nothing from this package beyond valid XML.

Status
------

[](#status)

Reader, writer, renderer, code lists and all 62 published business rules are done, covered against the 20 official reference invoices: every one round-trips to an equal object, every generated document validates against the official UBL 2.1 XSD, and rewriting never changes which rules a document breaks.

225 tests, 608 assertions.

Licence
-------

[](#licence)

MIT — see [LICENSE](LICENSE).

The licence covers the source code. The official artifacts under `research/` remain the work of their publishers under their own terms — see [NOTICE](NOTICE) and [research/README.md](research/README.md).

This package processes eRačun documents; it does not make anyone compliant with the Zakon o fiskalizaciji. No certificates are handled, stored or transmitted, and no connection is made to any government system.

Security reports: see [SECURITY.md](SECURITY.md).

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 71.4% 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

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3891cb1553b2a3e264c172a8ab016291be67c863bd191010a6ba7294517b8fe4?d=identicon)[stboris](/maintainers/stboris)

---

Top Contributors

[![stboris](https://avatars.githubusercontent.com/u/48769916?v=4)](https://github.com/stboris "stboris (5 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (2 commits)")

---

Tags

laravelfiskalizacijaublEN16931croatiaeracune-racun

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/stboris-laravel-eracun/health.svg)

```
[![Health](https://phpackages.com/badges/stboris-laravel-eracun/health.svg)](https://phpackages.com/packages/stboris-laravel-eracun)
```

###  Alternatives

[rtconner/laravel-likeable

Trait for Laravel Eloquent models to allow easy implementation of a 'like' or 'favorite' or 'remember' feature.

400396.7k5](/packages/rtconner-laravel-likeable)[slowlyo/owl-admin

基于 laravel、amis 开发的后台框架~

61315.0k26](/packages/slowlyo-owl-admin)[easybill/e-invoicing

A package to read and create EN16931 e-invoices or CIUS like: XRechnung, ZUGFeRD etc.

14158.3k](/packages/easybill-e-invoicing)

PHPackages © 2026

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