notebookvb
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
"""
|
||||
Stazeni detailu receptu (NacistPredpis) pro poslednich N receptu z Medicusu.
|
||||
|
||||
Spusteni:
|
||||
python 08StahnoutPredpisy.py # 10 receptu od 2025-01-01
|
||||
python 08StahnoutPredpisy.py --limit 50 # 50 receptu
|
||||
python 08StahnoutPredpisy.py --od 2026-01-01
|
||||
|
||||
Pauza mezi volanimi: 5 sekund.
|
||||
XML odpovedi se ukladaji do xml_archive/YYYY-MM-DD/ERP_kod.xml
|
||||
"""
|
||||
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime, timezone, date
|
||||
from pathlib import Path
|
||||
|
||||
import fdb
|
||||
from requests import Session
|
||||
from requests_pkcs12 import Pkcs12Adapter
|
||||
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(errors="replace")
|
||||
|
||||
# ── Konfigurace eRecept ──────────────────────────────────────────────────────
|
||||
PFX_FILE = r"C:\Users\vlado\PycharmProjects\Recepty\AMBSUKL214235369G_31DEC2024.pfx"
|
||||
PFX_PASS = "Vlado7309208104++"
|
||||
API_USER = "e08c89c6-2b1a-4eba-8ed9-4e3e63618379"
|
||||
API_PASS = "Buzalka@Vladimir2025"
|
||||
UZIVATEL = "E08C89C6-2B1A-4EBA-8ED9-4E3E63618379"
|
||||
PRACOVISTE = "00214235367"
|
||||
ENDPOINT = "https://lekar-soap.erecept.sukl.cz/cuer/Lekar"
|
||||
NAMESPACE = "http://www.sukl.cz/erp/201704"
|
||||
|
||||
PAUZA = 5 # sekund
|
||||
|
||||
# ── Konfigurace Firebird ─────────────────────────────────────────────────────
|
||||
FB_DSN = r'localhost:c:\medicus 3\data\medicus.fdb'
|
||||
FB_USER = 'SYSDBA'
|
||||
FB_PASS = 'masterkey'
|
||||
FB_CHARSET = 'win1250'
|
||||
|
||||
# ── Adresare ─────────────────────────────────────────────────────────────────
|
||||
XML_DIR = Path(__file__).parent / "xml_archive"
|
||||
|
||||
|
||||
def nacti_erp_kody(fb_conn, datum_od, limit, prijmeni=None):
|
||||
"""Nacte unikatni ID_Dokladu (erp kody) z Firebirdu — recept_epodani.erp."""
|
||||
sql = """
|
||||
SELECT FIRST ? r.datum, r.lek, r.dop, r.idpac,
|
||||
TRIM(kar.prijmeni) AS prijmeni, TRIM(kar.jmeno) AS jmeno,
|
||||
ep.erp
|
||||
FROM recept r
|
||||
JOIN recept_epodani ep ON r.id_epodani = ep.id
|
||||
JOIN kar ON r.idpac = kar.idpac
|
||||
WHERE r.datum >= ? AND ep.erp IS NOT NULL
|
||||
"""
|
||||
params = [limit, datum_od]
|
||||
if prijmeni:
|
||||
placeholders = ",".join(["?"] * len(prijmeni))
|
||||
sql += f" AND UPPER(TRIM(kar.prijmeni)) IN ({placeholders})"
|
||||
params.extend(p.upper() for p in prijmeni)
|
||||
sql += " ORDER BY r.datum DESC"
|
||||
cur = fb_conn.cursor()
|
||||
cur.execute(sql, params)
|
||||
rows = cur.fetchall()
|
||||
cur.close()
|
||||
# deduplikace dle erp kodu — zachovat prvni vyskyt (nejnovejsi datum)
|
||||
seen = set()
|
||||
unique = []
|
||||
for row in rows:
|
||||
erp = row[6]
|
||||
if erp not in seen:
|
||||
seen.add(erp)
|
||||
unique.append(row)
|
||||
return unique
|
||||
|
||||
|
||||
def volej_nacist_predpis(sess, erp_kod):
|
||||
"""Zavola NacistPredpis a vrati (status_code, response_text)."""
|
||||
id_zpravy = str(uuid.uuid4())
|
||||
odeslano = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S+00:00")
|
||||
|
||||
soap_body = (
|
||||
'<?xml version="1.0" encoding="UTF-8"?>'
|
||||
'<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">'
|
||||
'<soapenv:Body>'
|
||||
f'<NacteniPredpisuDotaz xmlns="{NAMESPACE}">'
|
||||
f'<Doklad>'
|
||||
f'<Pristupujici>'
|
||||
f'<Uzivatel>{UZIVATEL}</Uzivatel>'
|
||||
f'<Pracoviste>{PRACOVISTE}</Pracoviste>'
|
||||
f'</Pristupujici>'
|
||||
f'<Identifikator>'
|
||||
f'<ID_Dokladu>{erp_kod}</ID_Dokladu>'
|
||||
f'</Identifikator>'
|
||||
f'</Doklad>'
|
||||
f'<Zprava>'
|
||||
f'<ID_Zpravy>{id_zpravy}</ID_Zpravy>'
|
||||
f'<Verze>202501A</Verze>'
|
||||
f'<Odeslano>{odeslano}</Odeslano>'
|
||||
f'<SW_Klienta>MEDICUS_____</SW_Klienta>'
|
||||
f'</Zprava>'
|
||||
f'</NacteniPredpisuDotaz>'
|
||||
'</soapenv:Body>'
|
||||
'</soapenv:Envelope>'
|
||||
)
|
||||
|
||||
headers = {
|
||||
"Content-Type": 'text/xml; charset="UTF-8"',
|
||||
"SOAPAction": '"NacistPredpis"',
|
||||
"User-Agent": "Medicus",
|
||||
}
|
||||
|
||||
resp = sess.post(ENDPOINT, data=soap_body.encode("utf-8"), headers=headers, timeout=15)
|
||||
return resp.status_code, resp.text
|
||||
|
||||
|
||||
## ── Parametry (uprav zde) ────────────────────────────────────────────────────
|
||||
LIMIT = 100 # max pocet receptu ke stazeni
|
||||
DATUM_OD = "2025-01-01" # recepty od tohoto data
|
||||
PRIJMENI = ["Buzalka"] # filtr prijmeni (list), nebo None = vsichni
|
||||
## ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def main():
|
||||
datum_od = DATUM_OD
|
||||
dnes = date.today().isoformat()
|
||||
out_dir = XML_DIR / dnes
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Firebird
|
||||
print(f"Pripojuji Firebird...")
|
||||
fb = fdb.connect(dsn=FB_DSN, user=FB_USER, password=FB_PASS, charset=FB_CHARSET)
|
||||
rows = nacti_erp_kody(fb, datum_od, LIMIT, PRIJMENI)
|
||||
fb.close()
|
||||
print(f"Nacteno {len(rows)} receptu z Medicusu (od {datum_od})\n")
|
||||
|
||||
if not rows:
|
||||
print("Zadne recepty k stazeni.")
|
||||
return
|
||||
|
||||
# SOAP session
|
||||
sess = Session()
|
||||
sess.mount("https://", Pkcs12Adapter(pkcs12_filename=PFX_FILE, pkcs12_password=PFX_PASS))
|
||||
sess.auth = (API_USER, API_PASS)
|
||||
|
||||
ok = 0
|
||||
chyby = 0
|
||||
|
||||
for i, row in enumerate(rows, 1):
|
||||
datum_rec, lek, dop, idpac, prijmeni, jmeno, erp_kod = row
|
||||
lek_str = f"{lek} {dop}".strip() if dop else str(lek).strip()
|
||||
label = f"{prijmeni} {jmeno}".strip()
|
||||
|
||||
print(f"[{i:4d}/{len(rows)}] {label:30s} {erp_kod} ", end="", flush=True)
|
||||
|
||||
try:
|
||||
status, text = volej_nacist_predpis(sess, erp_kod)
|
||||
|
||||
if status == 200 and "<soap:Fault" not in text and "Fault>" not in text:
|
||||
xml_file = out_dir / f"{erp_kod}.xml"
|
||||
xml_file.write_text(text, encoding="utf-8")
|
||||
size_kb = len(text.encode("utf-8")) / 1024
|
||||
print(f"OK {size_kb:6.1f} KB {lek_str[:40]}")
|
||||
ok += 1
|
||||
else:
|
||||
# SOAP Fault nebo HTTP chyba
|
||||
chyba_short = text[:120].replace("\n", " ")
|
||||
print(f"CHYBA HTTP {status} {chyba_short}")
|
||||
# ulozit i chybovou odpoved
|
||||
xml_file = out_dir / f"{erp_kod}_CHYBA.xml"
|
||||
xml_file.write_text(text, encoding="utf-8")
|
||||
chyby += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"EXCEPTION {e}")
|
||||
chyby += 1
|
||||
|
||||
if i < len(rows):
|
||||
time.sleep(PAUZA)
|
||||
|
||||
print(f"\nHotovo: {ok} OK, {chyby} chyb")
|
||||
print(f"XML: {out_dir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,333 +1,202 @@
|
||||
# NacistPredpis — Funkční SOAP klient pro IS eRecept SÚKL
|
||||
|
||||
## Status
|
||||
**OVĚŘENO FUNKČNÍ** — 6. dubna 2026
|
||||
Odpověď serveru: `HTTP 200`, velikost: ~3.7 KB
|
||||
Schéma ověřeno proti: `schema1.xsd` verze `202501A` (dokumentace SÚKL 2025-04-24)
|
||||
Debug ověřen z: `MedicusDebug/eR_SuklMesage_bf002578-*.mime`
|
||||
Pipeline pro stažení **detailu jednotlivých receptů** z eRecept SÚKL API.
|
||||
Doplňuje existující hromadný lékový záznam (`NacistLekovyZaznam`) o údaje,
|
||||
které hromadný dotaz nevrací.
|
||||
|
||||
---
|
||||
|
||||
## Co tato operace dělá
|
||||
## Soubory
|
||||
|
||||
`NacistPredpis` je SOAP operace IS eRecept (SÚKL), která vrátí **kompletní detail jednoho konkrétního receptu** podle jeho ID.
|
||||
| Soubor | Co dělá |
|
||||
|--------|---------|
|
||||
| `NacistPredpis_FUNKCNI.py` | Stáhne detail **jednoho** receptu dle hardcoded ID_Dokladu (ruční test) |
|
||||
| `08StahnoutPredpisy.py` | **Hlavní skript** — načte ERP kódy z Medicusu, stáhne detaily, uloží XML |
|
||||
|
||||
Vrací vše: pacienta (jméno, adresa, rodné číslo, ZP), lékaře, ordinaci, předepsaný lék (ATC, dávkování, úhrada), stav receptu, výdej z lékárny.
|
||||
|
||||
Typické použití: lékař si zobrazí detail receptu, který sám vystavil, nebo ověří zda byl vydán.
|
||||
```
|
||||
NačteníPředpisuWithClaude/
|
||||
├── NacistPredpis_FUNKCNI.py ← test jednoho receptu
|
||||
├── 08StahnoutPredpisy.py ← hromadné stahování
|
||||
├── NacistPredpis_DOKUMENTACE.md ← tento soubor
|
||||
├── xml_archive/ ← archiv XML odpovědí (YYYY-MM-DD/ERP_KOD.xml)
|
||||
├── MedicusDebug/ ← zachycené SOAP požadavky z Medicusu
|
||||
└── Tests/ ← starší vývojové soubory
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Klíčové informace
|
||||
## Co NacistPredpis vrací navíc oproti NacistLekovyZaznam
|
||||
|
||||
### Endpoint (produkce)
|
||||
```
|
||||
https://lekar-soap.erecept.sukl.cz/cuer/Lekar
|
||||
```
|
||||
> Tato operace je na **starším** endpointu `/cuer/Lekar` — **bez** číslice 2.
|
||||
> Operace `NacistLekovyZaznam` je naopak na `/cuer/Lekar2`.
|
||||
Hromadný lékový záznam (`NacistLekovyZaznam`, endpoint `/cuer/Lekar2`)
|
||||
vrací seznam předpisů a výdejů za pacienta, ale **bez detailů o receptu jako celku**.
|
||||
|
||||
### Endpoint (testovací prostředí)
|
||||
```
|
||||
https://lekar-soap.test-erecept.sukl.cz/cuer/Lekar
|
||||
```
|
||||
`NacistPredpis` (endpoint `/cuer/Lekar`, namespace `201704`) vrací detail
|
||||
jednoho konkrétního receptu, včetně:
|
||||
|
||||
### SOAPAction
|
||||
```
|
||||
"NacistPredpis"
|
||||
```
|
||||
### Údaje o receptu (dokladu)
|
||||
|
||||
### XML namespace
|
||||
```
|
||||
http://www.sukl.cz/erp/201704
|
||||
```
|
||||
> Starší namespace z roku 2017 — tato operace existuje od počátku IS eRecept.
|
||||
> Na rozdíl od `NacistLekovyZaznam` (namespace `201912`) zde zůstává `201704`.
|
||||
| Pole | Popis |
|
||||
|------|-------|
|
||||
| `ID_Dokladu` | Alfanumerický kód receptu (např. `PPIBVF93285E`) |
|
||||
| `Stav` | Stav receptu: PREDEPSANY, CASTECNE_VYDANY, PLNE_VYDANY, ZRUSENY… |
|
||||
| `PlatnostDo` | Datum konce platnosti receptu |
|
||||
| `VypisDo` | Prodloužení platnosti výpisem |
|
||||
| `Akutni` | Příznak akutní péče |
|
||||
| `Rodina` | „Pro potřebu rodiny" / ad usum proprium |
|
||||
| `Opakovani` | Počet výdejů u opakovacích receptů |
|
||||
| `DruhPojisteni` | VEREJNE / OSTATNI |
|
||||
| `ModryPruh` | Omamné/psychotropní látky |
|
||||
| `Pozn` | Poznámka na receptu (max 1000 znaků) |
|
||||
| `ZapocitatelnyDoplatekZbyvaDoLimitu` | Zbývá do limitu doplatků pacienta |
|
||||
| `Zmena` / `Zalozeni` | Datetime poslední změny / vytvoření |
|
||||
|
||||
### Údaje o úhradě léku (per PLP)
|
||||
|
||||
| Pole | Popis |
|
||||
|------|-------|
|
||||
| `Uhrada` | ZAKLADNI / ZVYSENA / NEHRAZENY |
|
||||
| `Prekroceni` | Překročení limitu |
|
||||
|
||||
### Údaje o pacientovi
|
||||
|
||||
| Pole | Popis |
|
||||
|------|-------|
|
||||
| `CP` | Číslo pojištěnce (rodné číslo) |
|
||||
| `ZP` | Zdravotní pojišťovna (kód + název) |
|
||||
| `Adresa` | Kompletní adresa pacienta |
|
||||
| `Pohlavi` | M / Z |
|
||||
| `Telefon` | Telefonní číslo |
|
||||
| `Notifikace` | SMS / Email |
|
||||
|
||||
### Údaje o předepisujícím
|
||||
|
||||
| Pole | Popis |
|
||||
|------|-------|
|
||||
| `Odbornost` | Kód + název (např. 001 — všeobecné praktické lékařství) |
|
||||
| `Email` | Email lékaře |
|
||||
|
||||
### Zkrácený výdej
|
||||
|
||||
Odpověď obsahuje i sekci `Vydej[]` se zkrácenou informací o výdejích — název lékárny,
|
||||
jméno lékárníka (často „skryto"), datum vydeje, vydané léky.
|
||||
|
||||
---
|
||||
|
||||
## Porovnání s NacistLekovyZaznam
|
||||
## Porovnání operací
|
||||
|
||||
| | NacistPredpis | NacistLekovyZaznam |
|
||||
|---|---|---|
|
||||
| **Namespace** | `201704` | `201912` |
|
||||
| **Endpoint** | `/cuer/Lekar` | `/cuer/Lekar2` |
|
||||
| **SOAPAction** | `NacistPredpis` | `NacistLekovyZaznam` |
|
||||
| **Identifikace** | ID receptu (alfanumerický kód) | jméno + datum narození pacienta |
|
||||
| **Identifikace** | ID_Dokladu (alfanumerický kód receptu) | jméno + datum narození pacienta |
|
||||
| **Výsledek** | detail jednoho receptu | celý lékový záznam pacienta (roky) |
|
||||
| **Velikost odpovědi** | ~3.7 KB | ~227 KB |
|
||||
| **Velikost odpovědi** | ~3.5–4.5 KB | ~227 KB |
|
||||
|
||||
---
|
||||
|
||||
## Autentizace (dvojitá — stejná jako u všech operací)
|
||||
## Autentizace (stejná jako u všech operací eReceptu)
|
||||
|
||||
### 1. Klientský certifikát (TLS mutual auth)
|
||||
- Soubor: `AMBSUKL214235369G_31DEC2024.pfx`
|
||||
- Formát: PKCS#12 (`.pfx`)
|
||||
- Knihovna: `requests-pkcs12` → `Pkcs12Adapter`
|
||||
|
||||
### 2. HTTP Basic Auth
|
||||
- Uživatel: UUID lékaře
|
||||
- Heslo: osobní heslo lékaře do portálu eRecept
|
||||
| Parametr | Hodnota |
|
||||
|----------|---------|
|
||||
| Endpoint | `https://lekar-soap.erecept.sukl.cz/cuer/Lekar` |
|
||||
| mTLS certifikát | `AMBSUKL214235369G_31DEC2024.pfx` (platnost do 31. 12. 2026) |
|
||||
| HTTP Basic user | UUID lékaře `e08c89c6-2b1a-4eba-8ed9-4e3e63618379` |
|
||||
| SOAP operace | `NacistPredpis` |
|
||||
| XML namespace | `http://www.sukl.cz/erp/201704` |
|
||||
| Verze zprávy | `202501A` |
|
||||
|
||||
---
|
||||
|
||||
## Struktura SOAP dotazu
|
||||
## Zdroj ID_Dokladu — Medicus (Firebird)
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soapenv:Body>
|
||||
<NacteniPredpisuDotaz xmlns="http://www.sukl.cz/erp/201704">
|
||||
|
||||
<Doklad>
|
||||
<Pristupujici>
|
||||
<Uzivatel>E08C89C6-2B1A-4EBA-8ED9-4E3E63618379</Uzivatel> <!-- UUID lékaře -->
|
||||
<Pracoviste>00214235367</Pracoviste> <!-- IČP pracoviště -->
|
||||
</Pristupujici>
|
||||
<Identifikator>
|
||||
<ID_Dokladu>PPIBVF93285E</ID_Dokladu> <!-- ID receptu, 12–25 znaků -->
|
||||
<!-- alternativně: -->
|
||||
<!-- <ID_Podani>uuid-guid-formát</ID_Podani> -->
|
||||
</Identifikator>
|
||||
</Doklad>
|
||||
|
||||
<Zprava>
|
||||
<ID_Zpravy>bf002578-4e0d-48f9-86b4-ea3e21f280ec</ID_Zpravy>
|
||||
<Verze>202501A</Verze>
|
||||
<Odeslano>2026-04-06T07:24:04+00:00</Odeslano>
|
||||
<SW_Klienta>MEDICUS_____</SW_Klienta>
|
||||
</Zprava>
|
||||
|
||||
</NacteniPredpisuDotaz>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
```
|
||||
|
||||
### Parametry dotazu (ověřeno proti schema1.xsd verze 202501A)
|
||||
|
||||
| Element | Povinný | Omezení | Poznámka |
|
||||
|---|---|---|---|
|
||||
| `Uzivatel` | ✅ ano | UUID formát | UUID lékaře přidělené SÚKL |
|
||||
| `Pracoviste` | ✅ ano | — | IČP pracoviště lékaře |
|
||||
| `ID_Dokladu` | ❌ volitelný* | 12–25 znaků | alfanumerický kód receptu |
|
||||
| `ID_Podani` | ❌ volitelný* | UUID formát (36 znaků) | alternativní identifikátor podání |
|
||||
| `ID_Zpravy` | ✅ ano | UUID formát | nové UUID pro každý dotaz |
|
||||
| `Verze` | ✅ ano | — | aktuálně `202501A` |
|
||||
| `Odeslano` | ✅ ano | ISO 8601 | čas odeslání s časovou zónou |
|
||||
| `SW_Klienta` | ✅ ano | přesně 12 znaků | doplnit mezerami |
|
||||
|
||||
> *Uvést právě jedno z: `ID_Dokladu` nebo `ID_Podani` — viz chybový kód L001.
|
||||
|
||||
### Formát ID receptu (`ID_Dokladu`)
|
||||
|
||||
Alfanumerický kód délky 12–25 znaků, např. `PPIBVF93285E`.
|
||||
- Nesmí obsahovat písmena O (zaměnitelné s nulou), I (zaměnitelné s jedničkou), Y, Z
|
||||
- Nesmí obsahovat malá písmena ani diakritiku
|
||||
- Lékař jej vidí na průvodce receptu / v Medicusu
|
||||
|
||||
---
|
||||
|
||||
## Struktura odpovědi (ověřeno proti schema1.xsd verze 202501A)
|
||||
Alfanumerický kód receptu (ID_Dokladu) **není** v hromadném lékovém záznamu.
|
||||
Nachází se v tabulce **`RECEPT_EPODANI`** v Medicusu:
|
||||
|
||||
```
|
||||
NacteniPredpisuOdpoved
|
||||
├── Doklad
|
||||
│ ├── ID_Dokladu alfanumerický kód receptu
|
||||
│ ├── DatumVystaveni datum
|
||||
│ ├── PlatnostDo datum
|
||||
│ ├── VypisDo datum — volitelný (prodloužená platnost výpisem)
|
||||
│ ├── Akutni boolean — volitelný
|
||||
│ ├── Rodina boolean — volitelný
|
||||
│ ├── Preshranicni boolean — volitelný
|
||||
│ ├── Opakovani int — volitelný (opakovací předpis)
|
||||
│ │
|
||||
│ ├── Pacient
|
||||
│ │ ├── Totoznost
|
||||
│ │ │ ├── Jmeno (Prijmeni, Jmena)
|
||||
│ │ │ ├── DatumNarozeni
|
||||
│ │ │ ├── Adresa (ulice, číslo, obec, PSC, okres...)
|
||||
│ │ │ └── ROB způsob ztotožnění: ECD / JPDN / JPDNA
|
||||
│ │ ├── CP číslo pojištěnce (rodné číslo)
|
||||
│ │ ├── ZP Kod + Nazev zdravotní pojišťovny
|
||||
│ │ ├── Telefon volitelný
|
||||
│ │ ├── Notifikace SMS / Email — volitelný
|
||||
│ │ └── Pohlavi M / Z — volitelný
|
||||
│ │
|
||||
│ ├── Predepisujici
|
||||
│ │ ├── Lekar Kod (UUID) + Jmeno
|
||||
│ │ ├── ICZ 8 číslic
|
||||
│ │ ├── ICP 8 číslic
|
||||
│ │ ├── PZS Kod + Nazev + IC + Telefon + Adresa
|
||||
│ │ ├── Telefon
|
||||
│ │ ├── Email volitelný
|
||||
│ │ └── Odbornost Kod + Nazev
|
||||
│ │
|
||||
│ ├── Doporucujici volitelný — doporučující lékař
|
||||
│ │ ├── Jmeno
|
||||
│ │ ├── PZS
|
||||
│ │ └── Odbornost
|
||||
│ │
|
||||
│ ├── Revize volitelný — schválení revizním lékařem ZP
|
||||
│ │
|
||||
│ ├── PLP[] předepsané léčivé přípravky (1 nebo více)
|
||||
│ │ ├── Mnozstvi int, 1–9999
|
||||
│ │ ├── Navod dávkování, max 80 znaků (např. "1-1-0")
|
||||
│ │ ├── Uhrada ZAKLADNI / ZVYSENA / NEHRAZENY
|
||||
│ │ ├── HVLPReg volitelný — registrovaný lék
|
||||
│ │ │ ├── Kod SÚKL kód (7 číslic, např. "0087076")
|
||||
│ │ │ ├── ATC ATC kód (např. "R05CB15")
|
||||
│ │ │ ├── Nazev např. "ERDOMED"
|
||||
│ │ │ ├── Forma léková forma (např. "CPS DUR")
|
||||
│ │ │ ├── Sila síla (např. "300MG")
|
||||
│ │ │ ├── CestaPodani např. "POR"
|
||||
│ │ │ └── Baleni počet kusů v balení
|
||||
│ │ ├── HVLPNereg volitelný — neregistrovaný HVLP
|
||||
│ │ ├── IPLP volitelný — individuálně připravovaný LP
|
||||
│ │ ├── INN volitelný — generický název
|
||||
│ │ ├── Prekroceni boolean — překročení limitu
|
||||
│ │ ├── ID_LP_Zdroj int — ID zdroje LP
|
||||
│ │ └── ID_LP UUID položky předpisu
|
||||
│ │
|
||||
│ ├── Pozn poznámka, max 1000 znaků — volitelný
|
||||
│ ├── Stav stav receptu (viz níže)
|
||||
│ │
|
||||
│ ├── Vydej[] volitelný — výdeje z lékáren (zkrácená forma)
|
||||
│ │ ├── ID_Dokladu ID výdeje
|
||||
│ │ ├── ID_Dokladu_ERP ID receptu (zpětný odkaz)
|
||||
│ │ ├── DatumVydeje datum
|
||||
│ │ ├── NazevPZS název lékárny
|
||||
│ │ ├── JmenoVydavajiciho jméno lékárníka (může být "skryto")
|
||||
│ │ ├── VLP[] vydané léčivé přípravky
|
||||
│ │ │ ├── Mnozstvi
|
||||
│ │ │ ├── Nazev plný název léku (např. "ERDOMED 300MG CPS DUR 20")
|
||||
│ │ │ ├── Kod SÚKL kód
|
||||
│ │ │ ├── Navod
|
||||
│ │ │ └── IdLpErp UUID zpětný odkaz na PLP
|
||||
│ │ ├── Zmena datetime
|
||||
│ │ └── Zalozeni datetime
|
||||
│ │
|
||||
│ ├── Zmena datetime — poslední změna receptu
|
||||
│ ├── Zalozeni datetime — vytvoření receptu
|
||||
│ ├── DruhPojisteni VEREJNE / OSTATNI — volitelný
|
||||
│ ├── ModryPruh boolean — volitelný (omamné látky)
|
||||
│ ├── Papirovy boolean — volitelný
|
||||
│ └── ZapocitatelnyDoplatekZbyvaDoLimitu decimal — zbývající limit doplatků pacienta
|
||||
│
|
||||
└── Zprava
|
||||
├── ID_Zpravy nové UUID od serveru
|
||||
├── Verze
|
||||
├── Odeslano
|
||||
├── ID_Podani echo ID podání
|
||||
└── Prijato datetime — čas přijetí na serveru
|
||||
RECEPT.id_epodani → RECEPT_EPODANI.id
|
||||
RECEPT_EPODANI.erp = ID_Dokladu (např. "PPIBVF93285E")
|
||||
```
|
||||
|
||||
### Popis jednotlivých polí odpovědi (zdroj: `documentationCuer.html` SÚKL 2025-04-24)
|
||||
### SQL dotaz
|
||||
|
||||
| Pole | Typ | Povinné | Popis |
|
||||
|---|---|---|---|
|
||||
| `ID_Dokladu` | string 12–25 | ✅ | Identifikátor dokladu |
|
||||
| `DatumVystaveni` | date | ✅ | Datum vystavení eReceptu |
|
||||
| `PlatnostDo` | date | ✅ | Datum konce platnosti eReceptu určené při předepsání |
|
||||
| `VypisDo` | date | ❌ | Datum prodloužení platnosti eReceptu (lékárna může platnost prodloužit výpisem) |
|
||||
| `Akutni` | boolean | ❌ | Symbol Akutní péče nebo Neodkladná péče |
|
||||
| `Rodina` | boolean | ❌ | Symbol „Pro potřebu rodiny" nebo „Ad usum proprium" (lékař předepisuje sobě/rodině) |
|
||||
| `Preshranicni` | boolean | ❌ | Přeshraniční předpis *(nepoužívá se)* |
|
||||
| `Opakovani` | long | ❌ | Celkový počet výdejů u opakovacích receptů |
|
||||
| `Pacient` | nacteni_pacient_type | ✅ | Údaje o pacientovi |
|
||||
| `Predepisujici` | nacteni_predepisujici_type | ✅ | Údaje o předepisujícím lékaři |
|
||||
| `Doporucujici` | nacteni_doporucujici_type | ❌ | Údaje o doporučujícím lékaři |
|
||||
| `Revize` | nacteni_revize_type | ❌ | Schválení revizním lékařem ZP *(nepoužívá se)* |
|
||||
| `PLP` | nacteni_predepsany_lp_erp_type[] | ❌ | Předepsané položky (léky) |
|
||||
| `Pozn` | string 1–1000 | ❌ | Poznámka na eReceptu |
|
||||
| `UpozornitLekare` | enum | ❌ | Akceptace zpětného předání poznámky z lékárny — viz tabulka níže |
|
||||
| `Stav` | enum | ✅ | Stav eReceptu — viz tabulka níže |
|
||||
| `Vydej` | nacteni_zkraceny_vydej_erp_doklad_odpoved_type[] | ❌ | Seznam výdejů uskutečněných dosud na tento eRecept |
|
||||
| `Zmena` | dateTime | ✅ | Datum a čas poslední změny eReceptu |
|
||||
| `Zalozeni` | dateTime | ✅ | Datum a čas založení eReceptu |
|
||||
| `DruhPojisteni` | enum | ❌ | Druh pojištění pacienta — viz tabulka níže |
|
||||
| `ModryPruh` | boolean | ❌ | Předpis na LP s obsahem vysoce návykové látky (omamné/psychotropní) |
|
||||
| `Papirovy` | boolean | ❌ | Digitalizovaný papírový předpis *(nepoužívá se)* |
|
||||
| `ZapocitatelnyDoplatekZbyvaDoLimitu` | decimal 0–999999.99 | ❌ | Částka zbývající do max. ročního limitu započitatelných doplatků pacienta (po překročení limitu ZP hradí doplatky za pacienta) |
|
||||
```sql
|
||||
SELECT DISTINCT ep.erp, r.datum, r.lek, r.dop,
|
||||
TRIM(kar.prijmeni) AS prijmeni, TRIM(kar.jmeno) AS jmeno
|
||||
FROM recept r
|
||||
JOIN recept_epodani ep ON r.id_epodani = ep.id
|
||||
JOIN kar ON r.idpac = kar.idpac
|
||||
WHERE r.datum >= '2025-01-01' AND ep.erp IS NOT NULL
|
||||
ORDER BY r.datum DESC
|
||||
```
|
||||
|
||||
### Statistika (duben 2026)
|
||||
|
||||
- **13 571** receptů s ERP kódem od 1. 1. 2025
|
||||
- **13 578** receptů celkem (7 bez ERP kódu — papírové/neodeslané)
|
||||
|
||||
---
|
||||
|
||||
### Hodnoty stavu receptu (`Stav`) — enum `stav_elektronickeho_receptu`
|
||||
## 08StahnoutPredpisy.py — hlavní skript
|
||||
|
||||
| Hodnota | Popis |
|
||||
|---|---|
|
||||
| `KE_SCHVALENI` | *(Nepoužívá se)* Ke schválení zdravotní pojišťovnou |
|
||||
| `ZAMITNUTY` | *(Nepoužívá se)* Zamítnutý zdravotní pojišťovnou |
|
||||
| `PREDEPSANY` | Předepsaný — výchozí stav po založení, lze provést výdej ✅ |
|
||||
| `PRIPRAVOVANY` | Připravovaný — lékárna pracuje na výdeji (připravuje IPLP nebo objednala HVLP u distributora); výdej může provést jen tato lékárna; používá se i pro blokaci při technickém výpadku |
|
||||
| `CASTECNE_VYDANY` | Částečně vydaný — výdej proběhl, ale může následovat další (prodloužená platnost nebo opakovací recept s dalšími opakováními) |
|
||||
| `PLNE_VYDANY` | Plně vydaný — výdej dokončen, žádný další výdej nenásleduje ✅ |
|
||||
| `NEDOKONCENY_VYDEJ` | Nedokončený výdej — lékárna nevydala léčivo, ale zaznamenala doplňující informaci (důvod nevydání); lze použít pouze u prvního takového výdeje |
|
||||
| `UZAVRENY` | *(Nepoužívá se)* Uzavřený |
|
||||
|
||||
### Hodnoty druhu pojištění (`DruhPojisteni`) — enum `druh_pojisteni`
|
||||
|
||||
| Hodnota | Popis |
|
||||
|---|---|
|
||||
| `NEZADANO` | Nezadáno |
|
||||
| `VEREJNE` | Veřejné zdravotní pojištění (běžný případ v ČR) |
|
||||
| `SMLUVNI_PRIPOJISTENI` | Smluvní připojištění |
|
||||
| `CESTOVNI_PRIPOJISTENI` | Cestovní zdravotní připojištění |
|
||||
| `POJISTENI_EU` | Pojištění EU (pacient z jiného státu EU) |
|
||||
|
||||
### Hodnoty upozornění lékaře (`UpozornitLekare`) — enum `upozornit_lekare`
|
||||
|
||||
Lékárník může k výdeji přidat poznámku pro předepisujícího lékaře. Toto pole říká, zda lékař tuto funkci akceptoval a jak urgentně chce být informován.
|
||||
|
||||
| Hodnota | Popis |
|
||||
|---|---|
|
||||
| `BEZODKLADNE` | Upozornit lékaře bezodkladně (akceptuje BEZODKLADNE i PRISTI_NAVSTEVA) |
|
||||
| `PRISTI_NAVSTEVA` | Upozornit lékaře při příští návštěvě pacienta (akceptuje pouze PRISTI_NAVSTEVA) |
|
||||
| *(neuvedeno)* | Lékař neakceptoval zpětné předávání poznámek z lékárny |
|
||||
|
||||
### Hodnoty způsobu ztotožnění (`ROB`)
|
||||
|
||||
| Hodnota | Popis |
|
||||
|---|---|
|
||||
| `ECD` | elektronicky čitelný doklad (občanský průkaz/pas) |
|
||||
| `JPDN` | jednoznačný dle jména, příjmení, data a adresy |
|
||||
| `JPDNA` | jednoznačný dle jména, příjmení a data (bez adresy) |
|
||||
|
||||
---
|
||||
|
||||
## HTTP hlavičky
|
||||
### Parametry (editovat přímo v souboru)
|
||||
|
||||
```python
|
||||
headers = {
|
||||
"Content-Type": 'text/xml; charset="UTF-8"',
|
||||
"SOAPAction": '"NacistPredpis"', # uvozovky jsou součástí hodnoty!
|
||||
"User-Agent": "Medicus"
|
||||
}
|
||||
LIMIT = 100 # max počet receptů ke stažení
|
||||
DATUM_OD = "2025-01-01" # recepty od tohoto data
|
||||
PRIJMENI = ["Buzalka"] # filtr příjmení (list), nebo None = všichni
|
||||
```
|
||||
|
||||
### Co dělá
|
||||
|
||||
1. Připojí se k Firebirdu, načte unikátní ERP kódy (deduplikované — jeden recept může mít více léků)
|
||||
2. Pro každý ERP kód zavolá `NacistPredpis` přes SOAP API
|
||||
3. Uloží XML odpověď do `xml_archive/YYYY-MM-DD/{ERP_KOD}.xml`
|
||||
4. Chybové odpovědi uloží jako `{ERP_KOD}_CHYBA.xml`
|
||||
5. Pauza 5 sekund mezi voláními
|
||||
|
||||
### Výstup v konzoli
|
||||
|
||||
```
|
||||
[ 1/55] Buzalka Vladimír PPM5HM49EBF9 OK 3.6 KB EZETIMIB/ATORVASTATIN STADA 10MG/20MG TB
|
||||
[ 25/55] Buzalka Vladimír POMOTIAJ77PI CHYBA HTTP 500 D003 - Předpis zrušen...
|
||||
```
|
||||
|
||||
### Ošetření chyb
|
||||
|
||||
| Kód | Popis | Chování skriptu |
|
||||
|-----|-------|-----------------|
|
||||
| **D003** | Předpis byl zrušen lékařem | Uloží `_CHYBA.xml`, pokračuje dál |
|
||||
| HTTP 500 | SOAP Fault (obecný) | Uloží `_CHYBA.xml`, pokračuje dál |
|
||||
| Timeout / Exception | Síťová chyba | Vypíše EXCEPTION, pokračuje dál |
|
||||
|
||||
---
|
||||
|
||||
## Závislosti (Python)
|
||||
## Ověřeno (14. 4. 2026)
|
||||
|
||||
```
|
||||
requests
|
||||
requests-pkcs12
|
||||
```
|
||||
Testovací běh na receptech pacienta Buzalka od 1. 1. 2025:
|
||||
|
||||
- **55** unikátních ERP kódů nalezeno v Medicusu
|
||||
- **51** úspěšně staženo (OK)
|
||||
- **4** chyby D003 (zrušené recepty)
|
||||
- XML uloženy do `xml_archive/2026-04-14/`
|
||||
|
||||
---
|
||||
|
||||
## Soubor skriptu
|
||||
## Další kroky (plán)
|
||||
|
||||
`NacistPredpis_FUNKCNI.py` — funkční implementace, ověřeno 6. 4. 2026.
|
||||
1. **MySQL tabulka `recept`** — uložit detail receptu (stav, platnost, úhrada, pojišťovna…)
|
||||
2. **Parsování XML** — extrakce dat z odpovědí do MySQL
|
||||
3. **Inkrementální stahování** — procházet jen nové recepty od posledního běhu
|
||||
4. **Stažení všech 13 571 receptů** od 1. 1. 2025 (odhad: ~19 hodin při 5s pauze)
|
||||
|
||||
---
|
||||
|
||||
## Zdroje dokumentace SÚKL
|
||||
## XSD zdroje
|
||||
|
||||
| Soubor | Datum | Obsah |
|
||||
|---|---|---|
|
||||
| `MedicusDebug/eR_PostData.xml` | 2026-04-05 | reálný dotaz z Medicusu |
|
||||
| `MedicusDebug/eR_SuklMesage_*.mime` | 2026-04-05 | HTTP hlavičky + tělo dotazu |
|
||||
| `MedicusDebug/eR_SuklMesage_*_Resp.xml` | 2026-04-05 | reálná odpověď serveru |
|
||||
| `CUERLekar.wsdl` | 2025-04-24 | definice operace NacistPredpis |
|
||||
| `schema1.xsd` | 2025-04-24 | kompletní XSD schéma dotazu i odpovědi |
|
||||
Schéma verze `202501A`, soubory v `Dokumentace/2025-04-24/WSDL_XSD/`:
|
||||
|
||||
| Soubor | Obsah |
|
||||
|--------|-------|
|
||||
| `PRIORITNI_WEBOVE_SLUZBY/schema1.xsd` | `NacteniPredpisuDotaz`, `identifikace_dokladu_type`, `nacteni_predpisu_erp_odpoved_type` |
|
||||
| `NEPRIORITNI_WEBOVE_SLUZBY/CuerSchema.xsd` | `hvlp_type`, `zprava_type`, `jmeno_osoby_type` |
|
||||
|
||||
Reference in New Issue
Block a user