diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 03c669f..f1a9068 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -6,13 +6,19 @@ The following examples takes you through typical use cases with the :mod:`brreg` library. -Organization details by organization number -=========================================== +Querying Enhetsregisteret +========================= -To get details about an organization ("enhet") given its organization number: +To query Enhetsregisteret, you need to create a :class:`brreg.enhetsregisteret.Client` instance: >>> from brreg.enhetsregisteret import Client >>> client = Client() +>>> + +The client instance will ensure that the HTTP connection is reused across requests. + +To get details about an organization ("enhet") given its organization number: + >>> enhet = client.get_enhet('915501680') >>> enhet.organisasjonsnummer '915501680' @@ -21,5 +27,16 @@ To get details about an organization ("enhet") given its organization number: >>> enhet.organisasjonsform Organisasjonsform(kode='ASA', beskrivelse='Allmennaksjeselskap') >>> enhet.forretningsadresse -Adresse(land='Norge', landkode='NO', postnummer='0181', poststed='OSLO', adresse=['Torggata 7'], kommune='OSLO', kommunenummer='0301') +Adresse(adresse=['Torggata 7'], postnummer='0181', poststed='OSLO', kommunenummer='0301', kommune='OSLO', landkode='NO', land='Norge') +>>> + +To get details of a suborganization ("underenhet") given its organization number: + +>>> underenhet = client.get_underenhet('915659683') +>>> underenhet.organisasjonsnummer +'915659683' +>>> underenhet.antall_ansatte +91 +>>> underenhet.beliggenhetsadresse +Adresse(adresse=['Torggata 7'], postnummer='0181', poststed='OSLO', kommunenummer='0301', kommune='OSLO', landkode='NO', land='Norge') >>> diff --git a/src/brreg/enhetsregisteret/__init__.py b/src/brreg/enhetsregisteret/__init__.py index 7148bcc..a97313b 100644 --- a/src/brreg/enhetsregisteret/__init__.py +++ b/src/brreg/enhetsregisteret/__init__.py @@ -10,6 +10,7 @@ InstitusjonellSektorkode, Naeringskode, Organisasjonsform, + Underenhet, ) __all__ = [ @@ -21,4 +22,5 @@ "InstitusjonellSektorkode", "Naeringskode", "Organisasjonsform", + "Underenhet", ] diff --git a/src/brreg/enhetsregisteret/_client.py b/src/brreg/enhetsregisteret/_client.py index 0d5cede..e084dcb 100644 --- a/src/brreg/enhetsregisteret/_client.py +++ b/src/brreg/enhetsregisteret/_client.py @@ -6,7 +6,7 @@ import httpx from brreg import BrregError, BrregRestError -from brreg.enhetsregisteret._types import Enhet +from brreg.enhetsregisteret._types import Enhet, Underenhet if TYPE_CHECKING: from types import TracebackType @@ -66,3 +66,20 @@ def get_enhet(self, organisasjonsnummer: str) -> Optional[Enhet]: return None res.raise_for_status() return Enhet.model_validate_json(res.content) + + def get_underenhet(self, organisasjonsnummer: str) -> Optional[Underenhet]: + """Get :class:`Underenhet` given an organization number.""" + with error_handler(): + res = self._client.get( + f"/underenheter/{organisasjonsnummer}", + headers={ + "accept": ( + "application/vnd.brreg.enhetsregisteret.underenhet.v2+json;" + "charset=UTF-8" + ) + }, + ) + if res.status_code in (404, 410): + return None + res.raise_for_status() + return Underenhet.model_validate_json(res.content) diff --git a/src/brreg/enhetsregisteret/_types.py b/src/brreg/enhetsregisteret/_types.py index a3f6470..b2285be 100644 --- a/src/brreg/enhetsregisteret/_types.py +++ b/src/brreg/enhetsregisteret/_types.py @@ -197,3 +197,74 @@ class Enhet(BaseModel): #: Dato under-/enheten ble slettet slettedato: DateOrNone = None + + +class Underenhet(BaseModel): + """Enhet på laveste nivå i registreringsstrukturen i Enhetsregisteret. + + En underenhet kan ikke eksistere alene og har alltid knytning til en + hovedenhet. Identifiseres med organisasjonsnummer. + """ + + model_config = ConfigDict(alias_generator=to_camel) + + #: Underenhetens organisasjonsnummer + organisasjonsnummer: str + + #: Underenhetens navn + navn: str + + #: Underenhetens navn + organisasjonsform: Organisasjonsform + + #: Underenhetens hjemmeside + hjemmeside: Optional[str] = None + + #: Underenhetens postadresse + postadresse: Optional[Adresse] = None + + #: Underenhetens registreringsdato i Enhetsregisteret + registreringsdato_enhetsregisteret: DateOrNone = None + + #: Hvorvidt underenheten er registrert i MVA-registeret + registrert_i_mvaregisteret: Optional[bool] = None + + #: Underenheter som i utgangspunktet ikke er mva-pliktig, kan søke om + #: frivillig registrering i Merverdiavgiftsregisteret + frivillig_mva_registrert_beskrivelser: List[str] = Field(default_factory=list) + + #: Næringskode 1 + naeringskode1: Optional[Naeringskode] = None + + #: Næringskode 2 + naeringskode2: Optional[Naeringskode] = None + + #: Næringskode 3 + naeringskode3: Optional[Naeringskode] = None + + #: Hjelpeenhetskode + hjelpeenhetskode: Optional[Naeringskode] = None + + #: Antall ansatte + antall_ansatte: Optional[int] = None + + #: Angir om enheten har registrert ansatte + har_registrert_antall_ansatte: Optional[bool] = None + + #: Underenhetens overordnede enhet + overordnet_enhet: Optional[str] = None + + #: Underenhetens beliggenhetsadresse + beliggenhetsadresse: Optional[Adresse] = None + + #: Underenhetens oppstartsdato + oppstartsdato: DateOrNone = None + + #: Underenhetens dato for eierskifte + dato_eierskifte: DateOrNone = None + + #: Nedleggelsesdato for underenheten + nedleggelsesdato: DateOrNone = None + + #: Dato under-/enheten ble slettet + slettedato: DateOrNone = None diff --git a/tests/data/underenheter-details-deleted-response.json b/tests/data/underenheter-details-deleted-response.json new file mode 100644 index 0000000..c0964e8 --- /dev/null +++ b/tests/data/underenheter-details-deleted-response.json @@ -0,0 +1,20 @@ +{ + "organisasjonsnummer": "987123456", + "navn": "SLETTET UNDERENHET AS", + "organisasjonsform": { + "kode": "AAFY", + "beskrivelse": "Virksomhet til ikke-næringsdrivende person", + "_links": { + "self": { + "href": "http://localhost/enhetsregisteret/api/organisasjonsformer/AAFY" + } + } + }, + "slettedato": "2017-10-20", + "nedleggelsesdato": "2017-10-05", + "_links": { + "self": { + "href": "http://localhost/enhetsregisteret/api/underenheter/987123456" + } + } +} diff --git a/tests/data/underenheter-details-response.json b/tests/data/underenheter-details-response.json new file mode 100644 index 0000000..1d4a54c --- /dev/null +++ b/tests/data/underenheter-details-response.json @@ -0,0 +1,49 @@ +{ + "organisasjonsnummer": "776655441", + "navn": "SESAM STASJON", + "organisasjonsform": { + "kode": "BEDR", + "beskrivelse": "Bedrift", + "_links": { + "self": { + "href": "http://localhost/enhetsregisteret/api/organisasjonsformer/BEDR" + } + } + }, + "postadresse": { + "land": "Norge", + "landkode": "NO", + "postnummer": "0122", + "poststed": "OSLO", + "adresse": ["c/o reder K. Rusing", "Postboks 1752 Vika", ""], + "kommune": "OSLO", + "kommunenummer": "0301" + }, + "registreringsdatoEnhetsregisteret": "2017-10-20", + "registrertIMvaregisteret": true, + "naeringskode1": { + "kode": "52.292", + "beskrivelse": "Skipsmegling" + }, + "antallAnsatte": 50, + "harRegistrertAntallAnsatte": true, + "overordnetEnhet": "112233445", + "beliggenhetsadresse": { + "land": "Norge", + "landkode": "NO", + "postnummer": "0122", + "poststed": "OSLO", + "adresse": ["Tyvholmen 1", null, null], + "kommune": "OSLO", + "kommunenummer": "0301" + }, + "nedleggelsesdato": "2018-10-20", + "_links": { + "self": { + "href": "http://localhost/enhetsregisteret/api/underenheter/776655441" + }, + "overordnetEnhet": { + "href": "http://localhost/enhetsregisteret/api/enheter/112233445" + } + } +} diff --git a/tests/enhetsregisteret/test_get_underenhet.py b/tests/enhetsregisteret/test_get_underenhet.py index e69de29..dd6fc84 100644 --- a/tests/enhetsregisteret/test_get_underenhet.py +++ b/tests/enhetsregisteret/test_get_underenhet.py @@ -0,0 +1,67 @@ +from datetime import date +from pathlib import Path + +from pytest_httpx import HTTPXMock + +from brreg import enhetsregisteret + +DATA_DIR = Path(__file__).parent.parent / "data" + + +def test_get_underenhet(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response( # pyright: ignore[reportUnknownMemberType] + method="GET", + url="https://data.brreg.no/enhetsregisteret/api/underenheter/776655441", + status_code=200, + headers={"content-type": "application/json"}, + content=(DATA_DIR / "underenheter-details-response.json").read_bytes(), + ) + + org = enhetsregisteret.Client().get_underenhet("776655441") + + assert org is not None + assert org.organisasjonsnummer == "776655441" + assert org.navn == "SESAM STASJON" + assert org.hjemmeside is None + assert org.registreringsdato_enhetsregisteret == date(2017, 10, 20) + assert org.registrert_i_mvaregisteret is True + assert org.naeringskode1 == enhetsregisteret.Naeringskode( + kode="52.292", beskrivelse="Skipsmegling" + ) + assert org.antall_ansatte == 50 + assert org.har_registrert_antall_ansatte is True + assert org.beliggenhetsadresse == enhetsregisteret.Adresse( + land="Norge", + landkode="NO", + postnummer="0122", + poststed="OSLO", + adresse=["Tyvholmen 1", None, None], + kommune="OSLO", + kommunenummer="0301", + ) + assert org.oppstartsdato is None + assert org.dato_eierskifte is None + assert org.nedleggelsesdato == date(2018, 10, 20) + assert org.slettedato is None + + +def test_get_underenhet_when_deleted(httpx_mock: HTTPXMock) -> None: + httpx_mock.add_response( # pyright: ignore[reportUnknownMemberType] + method="GET", + url="https://data.brreg.no/enhetsregisteret/api/underenheter/987123456", + status_code=200, + headers={"content-type": "application/json"}, + content=(DATA_DIR / "underenheter-details-deleted-response.json").read_bytes(), + ) + + org = enhetsregisteret.Client().get_underenhet("987123456") + + assert org is not None + assert org.organisasjonsnummer == "987123456" + assert org.navn == "SLETTET UNDERENHET AS" + assert org.organisasjonsform == enhetsregisteret.Organisasjonsform( + kode="AAFY", + beskrivelse="Virksomhet til ikke-næringsdrivende person", + utgaatt=None, + ) + assert org.slettedato == date(2017, 10, 20)