Metadata-Version: 2.4
Name: harriers-lustr
Version: 0.1.0
Summary: Jurisdiction-agnostic company-register lookup (CZ ARES, …).
Project-URL: Repository, https://gitlab.com/harriers/lustr
Author: Lukáš Dorňák
License-Expression: MIT
License-File: LICENSE
Keywords: ares,business-register,company,czech,ico,registry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business :: Financial
Requires-Python: >=3.10
Requires-Dist: httpx>=0.24
Provides-Extra: test
Requires-Dist: pytest>=8; extra == 'test'
Requires-Dist: respx>=0.21; extra == 'test'
Description-Content-Type: text/markdown

# lustr

Jurisdiction-agnostic **company-register lookup**. Give it a jurisdiction and a
registration id (CZ: IČO), get back jurisdiction-neutral company data — name, VAT id,
legal form, seat address, responsible tax office — through one small interface.

Bundled providers: **CZ → ARES** and **SK → RPO** (both real, public REST APIs).
Framework-free (only depends on `httpx`); an optional Django cache adapter is included.

## Install

From PyPI (the distribution is named `harriers-lustr`; the import stays `lustr`):

```bash
pip install harriers-lustr
```

To depend on it from another project, pin it by version like any other PyPI package:

```toml
# pyproject.toml / requirements.txt
harriers-lustr>=0.1
```

Installing straight from the repository also works (e.g. before a release is on PyPI):

```bash
pip install "harriers-lustr @ git+https://gitlab.com/harriers/lustr@v0.1.0"
```

> **Renamed from `lustr`.** Earlier releases were published as `lustr` in this
> project's GitLab package registry; that registry no longer receives releases, so
> switch any `lustr` dependency to `harriers-lustr` from PyPI. Do **not** `pip
> install lustr` — the name `lustr` on public PyPI belongs to an unrelated project.

## Usage

```python
import lustr

# Method 1 — the registration-id format for a jurisdiction (drives input UI).
fmt = lustr.id_format("CZ")
print(fmt.label, fmt.example)          # "IČO" "27082440"

# Method 2 — the company data for a registration id.
subject = lustr.fetch_subject("CZ", "27082440")
print(subject.name)                    # "Alza.cz a.s."
print(subject.vat_id)                  # "CZ27082440"
print(subject.legal_form_name)         # "Akciová společnost"
print(subject.tax_office_code)         # "451"  (ÚFO resolved from the seat's kraj)
```

`fetch_subject` returns a frozen [`RegistrySubject`](src/lustr/base.py) dataclass and
raises typed errors: `InvalidRegistrationId`, `RegistryNotFound`, `RegistryUnavailable`,
`UnknownJurisdiction` (all subclasses of `RegistryError`).

`lustr.jurisdictions()` lists the supported jurisdictions for a picker.

### Slovakia

The identity (name, legal form, seat) comes from **RPO**, which needs no auth. The VAT
id (IČ DPH) lives in a separate register — the Financial Administration's OpenData
portal — which needs a free, self-service API key:

```python
lustr.configure_sk(fs_dph_key="…")     # or set LUSTR_SK_DPH_KEY in the environment
```

The key is **required**: the VAT lookup is a step of every SK company lookup, so a
missing key or an unreachable FS register raises `RegistryUnavailable` instead of
returning a subject. That way an empty `vat_id` always means "not a VAT payer" and never
"could not be checked".

## Caching

Lookups are cached (12h TTL) so repeated queries don't hit the register. By default the
cache is a process-local in-memory store — zero config. To share a cache across
processes, inject any backend with `get`/`set`/`delete`:

```python
lustr.configure_cache(my_cache)
```

### Django

An adapter over `django.core.cache` ships in `lustr.contrib.django` (imports Django
lazily, so it stays an optional dependency). Wire it once at startup:

```python
import lustr
from lustr.contrib.django import DjangoCache

lustr.configure_cache(DjangoCache())          # the "default" cache alias
lustr.configure_cache(DjangoCache("redis"))   # a named alias
```

Mapping a `RegistrySubject` onto your own models (an `Organization`, a contact/party,
…) stays in your project — lustr only returns the neutral data.

## Adding a jurisdiction

Subclass `RegistryProvider` (implement `id_format` + `fetch`, optionally `normalize_id`)
and register it:

```python
import lustr

class MyProvider(lustr.RegistryProvider):
    jurisdiction = "PL"
    name = "Polska"
    register_name = "KRS"
    def id_format(self): ...
    def fetch(self, registration_id): ...

lustr.register_provider(MyProvider())
```

## Development

```bash
pip install -e ".[test]"
pytest
```

## Releasing

The version is derived from the git tag (`hatch-vcs`). To cut a release:

```bash
git tag v0.1.0
git push origin v0.1.0
```

The CI `publish` job builds the sdist+wheel and uploads them to PyPI via
[trusted publishing](https://docs.pypi.org/trusted-publishers/) (OIDC — no API token
stored in CI). One-time prerequisite: the PyPI project `harriers-lustr` must list this
GitLab repo as a trusted publisher — for the very first release, register a
[pending publisher](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/)
on PyPI for gitlab.com namespace `harriers`, project `lustr`.

## License

MIT — see [LICENSE](LICENSE).
