feat: Add all 10 missing KRS data fields

Extend KRS data collection and display:
1. NIP - displayed in company profile
2. REGON - displayed in company profile
3. Email from KRS (adresPocztyElektronicznej)
4. WWW from KRS (adresStronyInternetowej)
5. ePUAP address (adresDoDoreczenElektronicznychWpisanyDoBAE)
6. Company agreement date (data zawarcia umowy)
7. Company duration (czas trwania spółki)
8. Share information (informacja o udziałach)
9. Financial statements history (sprawozdania finansowe)
10. Full PKD codes with class/subclass (e.g., 62.03.Z)
11. Court registry data (sygnatura, sąd, rok obrotowy)

Updated krs_api_service.py:
- Extended KRSCompanyData dataclass with new fields
- Updated parse_krs_response() to extract all data
- PKD now returns dict with kod, opis, glowna

Updated templates/company_detail.html:
- Display NIP and REGON from KRS
- Contact section with email, www, ePUAP
- Company agreement section
- Financial statements history grid
- Court registry information
- Improved PKD display with full codes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-02-01 07:56:56 +01:00
parent 30e3883704
commit 66bb66dd36
2 changed files with 227 additions and 11 deletions

View File

@ -36,6 +36,12 @@ class KRSCompanyData:
powiat: Optional[str]
gmina: Optional[str]
kraj: str
poczta: Optional[str] # NOWE
# Contact from KRS (NOWE)
email_krs: Optional[str]
www_krs: Optional[str]
adres_epuap: Optional[str]
# Capital
kapital_zakladowy: Optional[float]
@ -46,17 +52,31 @@ class KRSCompanyData:
data_ostatniego_wpisu: Optional[str]
numer_ostatniego_wpisu: Optional[int]
# Company agreement/statute (NOWE)
data_umowy_spolki: Optional[str]
czas_trwania_spolki: Optional[str]
informacja_o_udzialach: Optional[str]
# Management (anonymized in Open API)
zarzad: List[Dict[str, str]]
nazwa_organu: Optional[str] # NOWE
sposob_reprezentacji: Optional[str]
# Shareholders (anonymized in Open API)
wspolnicy: List[Dict[str, Any]]
# Other
przedmiot_dzialalnosci: List[str]
przedmiot_dzialalnosci: List[Dict[str, str]] # ZMIANA: teraz słownik z pełnymi kodami
czy_opp: bool
# Financial statements (NOWE)
sprawozdania_finansowe: List[Dict[str, str]]
# Court/Registry info (NOWE)
sygnatura_akt: Optional[str]
sad_rejestrowy: Optional[str]
dzien_konczacy_rok_obrotowy: Optional[str]
# Metadata
data_odpisu: str
stan_z_dnia: str
@ -80,6 +100,12 @@ class KRSCompanyData:
'powiat': self.powiat,
'gmina': self.gmina,
'kraj': self.kraj,
'poczta': self.poczta,
},
'kontakt_krs': {
'email': self.email_krs,
'www': self.www_krs,
'adres_epuap': self.adres_epuap,
},
'kapital': {
'zakladowy': self.kapital_zakladowy,
@ -90,11 +116,23 @@ class KRSCompanyData:
'ostatniego_wpisu': self.data_ostatniego_wpisu,
'numer_ostatniego_wpisu': self.numer_ostatniego_wpisu,
},
'umowa_spolki': {
'data_umowy': self.data_umowy_spolki,
'czas_trwania': self.czas_trwania_spolki,
'informacja_o_udzialach': self.informacja_o_udzialach,
},
'zarzad': self.zarzad,
'nazwa_organu': self.nazwa_organu,
'sposob_reprezentacji': self.sposob_reprezentacji,
'wspolnicy': self.wspolnicy,
'przedmiot_dzialalnosci': self.przedmiot_dzialalnosci,
'czy_opp': self.czy_opp,
'sprawozdania_finansowe': self.sprawozdania_finansowe,
'rejestr': {
'sygnatura_akt': self.sygnatura_akt,
'sad_rejestrowy': self.sad_rejestrowy,
'dzien_konczacy_rok_obrotowy': self.dzien_konczacy_rok_obrotowy,
},
'metadata': {
'data_odpisu': self.data_odpisu,
'stan_z_dnia': self.stan_z_dnia,
@ -161,7 +199,7 @@ def parse_krs_response(data: Dict[str, Any]) -> Optional[KRSCompanyData]:
dane_podmiotu = dzial1.get('danePodmiotu', {})
identyfikatory = dane_podmiotu.get('identyfikatory', {})
# Address
# Address and Contact
siedziba_adres = dzial1.get('siedzibaIAdres', {})
siedziba = siedziba_adres.get('siedziba', {})
adres = siedziba_adres.get('adres', {})
@ -170,6 +208,16 @@ def parse_krs_response(data: Dict[str, Any]) -> Optional[KRSCompanyData]:
kapital = dzial1.get('kapital', {})
kapital_zakladowy = kapital.get('wysokoscKapitaluZakladowego', {})
# Company agreement/statute
umowa_statut = dzial1.get('umowaStatut', {})
pozostale = dzial1.get('pozostaleInformacje', {})
# Get first date of company agreement
data_umowy = None
umowy_list = umowa_statut.get('informacjaOZawarciuZmianieUmowyStatutu', [])
if umowy_list:
data_umowy = umowy_list[0].get('zawarcieZmianaUmowyStatutu')
# Management
reprezentacja = dzial2.get('reprezentacja', {})
sklad_zarzadu = reprezentacja.get('sklad', [])
@ -181,7 +229,7 @@ def parse_krs_response(data: Dict[str, Any]) -> Optional[KRSCompanyData]:
'imie_drugie': osoba.get('imiona', {}).get('imieDrugie', ''),
'nazwisko': osoba.get('nazwisko', {}).get('nazwiskoICzlon', ''),
'funkcja': osoba.get('funkcjaWOrganie', ''),
# Note: Data is anonymized in Open API
'zawieszona': osoba.get('czyZawieszona', False),
})
# Shareholders
@ -195,13 +243,36 @@ def parse_krs_response(data: Dict[str, Any]) -> Optional[KRSCompanyData]:
'calosc_udzialow': wspolnik.get('czyPosiadaCaloscUdzialow', False),
})
# Business activities (PKD)
# Business activities (PKD) - with full codes
przedmiot = []
przedmiot_dzial = dzial3.get('przedmiotDzialalnosci', {})
for pkd in przedmiot_dzial.get('przedmiotPrzewazajacejDzialalnosci', []):
przedmiot.append(f"{pkd.get('kodDzial', '')} - {pkd.get('opis', '')} (główna)")
kod_pelny = f"{pkd.get('kodDzial', '')}.{pkd.get('kodKlasa', '')}.{pkd.get('kodPodklasa', '')}"
przedmiot.append({
'kod': kod_pelny,
'opis': pkd.get('opis', ''),
'glowna': True,
})
for pkd in przedmiot_dzial.get('przedmiotPozostalejDzialalnosci', []):
przedmiot.append(f"{pkd.get('kodDzial', '')} - {pkd.get('opis', '')}")
kod_pelny = f"{pkd.get('kodDzial', '')}.{pkd.get('kodKlasa', '')}.{pkd.get('kodPodklasa', '')}"
przedmiot.append({
'kod': kod_pelny,
'opis': pkd.get('opis', ''),
'glowna': False,
})
# Financial statements
sprawozdania = []
wzmianki = dzial3.get('wzmiankiOZlozonychDokumentach', {})
for sf in wzmianki.get('wzmiankaOZlozeniuRocznegoSprawozdaniaFinansowego', []):
sprawozdania.append({
'data_zlozenia': sf.get('dataZlozenia', ''),
'za_okres': sf.get('zaOkresOdDo', ''),
})
# Fiscal year end
rok_obrotowy = dzial3.get('informacjaODniuKonczacymRokObrotowy', {})
dzien_konczacy = rok_obrotowy.get('dzienKonczacyPierwszyRokObrotowy')
# Parse capital value
kapital_value = None
@ -219,6 +290,7 @@ def parse_krs_response(data: Dict[str, Any]) -> Optional[KRSCompanyData]:
regon=identyfikatory.get('regon'),
forma_prawna=dane_podmiotu.get('formaPrawna', ''),
# Address
ulica=adres.get('ulica'),
nr_domu=adres.get('nrDomu'),
nr_lokalu=adres.get('nrLokalu'),
@ -228,20 +300,48 @@ def parse_krs_response(data: Dict[str, Any]) -> Optional[KRSCompanyData]:
powiat=siedziba.get('powiat'),
gmina=siedziba.get('gmina'),
kraj=adres.get('kraj', 'POLSKA'),
poczta=adres.get('poczta'),
# Contact from KRS
email_krs=siedziba_adres.get('adresPocztyElektronicznej'),
www_krs=siedziba_adres.get('adresStronyInternetowej'),
adres_epuap=siedziba_adres.get('adresDoDoreczenElektronicznychWpisanyDoBAE'),
# Capital
kapital_zakladowy=kapital_value,
kapital_waluta=kapital_zakladowy.get('waluta', 'PLN'),
# Dates
data_rejestracji=naglowek.get('dataRejestracjiWKRS'),
data_ostatniego_wpisu=naglowek.get('dataOstatniegoWpisu'),
numer_ostatniego_wpisu=naglowek.get('numerOstatniegoWpisu'),
# Company agreement
data_umowy_spolki=data_umowy,
czas_trwania_spolki=pozostale.get('czasNaJakiUtworzonyZostalPodmiot'),
informacja_o_udzialach=pozostale.get('informacjaOLiczbieUdzialow'),
# Management
zarzad=zarzad,
nazwa_organu=reprezentacja.get('nazwaOrganu'),
sposob_reprezentacji=reprezentacja.get('sposobReprezentacji'),
# Shareholders
wspolnicy=wspolnicy,
# Business activities
przedmiot_dzialalnosci=przedmiot,
czy_opp=dane_podmiotu.get('czyPosiadaStatusOPP', False),
# Financial statements
sprawozdania_finansowe=sprawozdania,
# Court/Registry info
sygnatura_akt=naglowek.get('sygnaturaAktSprawyDotyczacejOstatniegoWpisu'),
sad_rejestrowy=naglowek.get('oznaczenieSaduDokonujacegoOstatniegoWpisu'),
dzien_konczacy_rok_obrotowy=dzien_konczacy,
# Metadata
data_odpisu=naglowek.get('dataCzasOdpisu', ''),
stan_z_dnia=naglowek.get('stanZDnia', ''),
)

View File

@ -1371,6 +1371,26 @@
</div>
</div>
<!-- NIP z KRS -->
{% if krs.nip %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #3b82f6;">
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em;">NIP</div>
<div style="font-size: var(--font-size-xl); font-weight: 700; color: #3b82f6; font-family: monospace; margin-top: 4px;">
{{ krs.nip[:3] }}-{{ krs.nip[3:6] }}-{{ krs.nip[6:8] }}-{{ krs.nip[8:] }}
</div>
</div>
{% endif %}
<!-- REGON z KRS -->
{% if krs.regon %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #0891b2;">
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em;">REGON</div>
<div style="font-size: var(--font-size-xl); font-weight: 700; color: #0891b2; font-family: monospace; margin-top: 4px;">
{{ krs.regon }}
</div>
</div>
{% endif %}
<!-- Data rejestracji w KRS -->
{% if company.krs_registration_date or (krs.daty and krs.daty.rejestracji) %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #059669;">
@ -1391,10 +1411,66 @@
</div>
{% endif %}
<!-- Dane kontaktowe z KRS -->
{% if krs.kontakt_krs and (krs.kontakt_krs.email or krs.kontakt_krs.www or krs.kontakt_krs.adres_epuap) %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #10b981; grid-column: span 2;">
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--spacing-md);">Dane kontaktowe z KRS</div>
<div style="display: flex; flex-wrap: wrap; gap: var(--spacing-lg);">
{% if krs.kontakt_krs.email %}
<div>
<div style="font-size: var(--font-size-xs); color: var(--text-muted);">Email</div>
<a href="mailto:{{ krs.kontakt_krs.email }}" style="color: #10b981; font-weight: 600;">{{ krs.kontakt_krs.email }}</a>
</div>
{% endif %}
{% if krs.kontakt_krs.www %}
<div>
<div style="font-size: var(--font-size-xs); color: var(--text-muted);">Strona WWW</div>
<a href="https://{{ krs.kontakt_krs.www|lower }}" target="_blank" style="color: #10b981; font-weight: 600;">{{ krs.kontakt_krs.www }}</a>
</div>
{% endif %}
{% if krs.kontakt_krs.adres_epuap %}
<div>
<div style="font-size: var(--font-size-xs); color: var(--text-muted);">Adres ePUAP (BAE)</div>
<span style="font-family: monospace; font-size: var(--font-size-sm); color: var(--text-secondary);">{{ krs.kontakt_krs.adres_epuap }}</span>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Umowa spółki -->
{% if krs.umowa_spolki and (krs.umowa_spolki.data_umowy or krs.umowa_spolki.czas_trwania) %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #7c3aed; grid-column: span 2;">
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--spacing-md);">Umowa spółki</div>
<div style="display: flex; flex-wrap: wrap; gap: var(--spacing-lg);">
{% if krs.umowa_spolki.data_umowy %}
<div>
<div style="font-size: var(--font-size-xs); color: var(--text-muted);">Data zawarcia/zmiany umowy</div>
<div style="font-weight: 600; color: #7c3aed;">{{ krs.umowa_spolki.data_umowy }}</div>
</div>
{% endif %}
{% if krs.umowa_spolki.czas_trwania %}
<div>
<div style="font-size: var(--font-size-xs); color: var(--text-muted);">Czas trwania spółki</div>
<div style="font-weight: 600; color: var(--text-primary);">{{ krs.umowa_spolki.czas_trwania }}</div>
</div>
{% endif %}
{% if krs.umowa_spolki.informacja_o_udzialach %}
<div>
<div style="font-size: var(--font-size-xs); color: var(--text-muted);">Informacja o udziałach</div>
<div style="font-weight: 600; color: var(--text-primary);">{{ krs.umowa_spolki.informacja_o_udzialach }}</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Sposób reprezentacji -->
{% if company.krs_representation or krs.sposob_reprezentacji %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #8b5cf6; grid-column: span 2;">
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--spacing-sm);">Sposób reprezentacji</div>
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--spacing-sm);">
{% if krs.nazwa_organu %}{{ krs.nazwa_organu }} - {% endif %}Sposób reprezentacji
</div>
<div style="font-size: var(--font-size-base); color: var(--text-primary); line-height: 1.6;">
{{ company.krs_representation or krs.sposob_reprezentacji }}
</div>
@ -1475,11 +1551,33 @@
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--spacing-md);">
Przedmiot działalności z KRS ({{ krs.przedmiot_dzialalnosci|length }} PKD)
</div>
<div style="display: flex; flex-wrap: wrap; gap: var(--spacing-sm); max-height: 200px; overflow-y: auto;">
<div style="display: flex; flex-direction: column; gap: var(--spacing-sm); max-height: 250px; overflow-y: auto;">
{% for pkd in krs.przedmiot_dzialalnosci %}
<span style="background: var(--surface); color: var(--text-secondary); padding: 4px 10px; border-radius: var(--radius); font-size: var(--font-size-sm); border: 1px solid var(--border);">
{{ pkd }}
<div style="display: flex; align-items: center; gap: var(--spacing-sm); padding: var(--spacing-sm); background: var(--surface); border-radius: var(--radius); border: 1px solid var(--border);">
<span style="font-family: monospace; font-weight: 700; color: {% if pkd.glowna %}#a855f7{% else %}var(--text-secondary){% endif %}; min-width: 70px;">
{{ pkd.kod if pkd.kod else pkd }}{% if pkd.glowna %} ★{% endif %}
</span>
<span style="font-size: var(--font-size-sm); color: var(--text-primary);">
{{ pkd.opis if pkd.opis else '' }}{% if pkd.glowna %} <em style="color: #a855f7;">(główna)</em>{% endif %}
</span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Sprawozdania finansowe -->
{% if krs.sprawozdania_finansowe and krs.sprawozdania_finansowe|length > 0 %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #14b8a6; grid-column: span 2;">
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--spacing-md);">
Sprawozdania finansowe ({{ krs.sprawozdania_finansowe|length }})
</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--spacing-md);">
{% for sf in krs.sprawozdania_finansowe %}
<div style="padding: var(--spacing-sm); background: var(--surface); border-radius: var(--radius); border: 1px solid var(--border);">
<div style="font-size: var(--font-size-xs); color: var(--text-muted);">Złożono: {{ sf.data_zlozenia }}</div>
<div style="font-size: var(--font-size-sm); font-weight: 600; color: #14b8a6; margin-top: 2px;">{{ sf.za_okres }}</div>
</div>
{% endfor %}
</div>
</div>
@ -1514,6 +1612,24 @@
</div>
{% endif %}
<!-- Dane sądu rejestrowego -->
{% if krs.rejestr and (krs.rejestr.sygnatura_akt or krs.rejestr.sad_rejestrowy or krs.rejestr.dzien_konczacy_rok_obrotowy) %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #78716c;">
<div style="font-size: var(--font-size-sm); color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: var(--spacing-sm);">Dane rejestrowe</div>
<div style="font-size: var(--font-size-sm); color: var(--text-muted);">
{% if krs.rejestr.sygnatura_akt %}
<div>Sygnatura akt: <strong style="font-family: monospace;">{{ krs.rejestr.sygnatura_akt }}</strong></div>
{% endif %}
{% if krs.rejestr.sad_rejestrowy %}
<div>Sąd dokonujący wpisu: <strong>{{ krs.rejestr.sad_rejestrowy }}</strong></div>
{% endif %}
{% if krs.rejestr.dzien_konczacy_rok_obrotowy %}
<div>Dzień kończący rok obrotowy: <strong>{{ krs.rejestr.dzien_konczacy_rok_obrotowy }}</strong></div>
{% endif %}
</div>
</div>
{% endif %}
<!-- Metadane KRS -->
{% if krs.metadata or krs.daty %}
<div style="background: var(--background); border-radius: var(--radius-lg); padding: var(--spacing-lg); border-left: 4px solid #94a3b8;">