Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
NacistPredpis — Funkční SOAP klient pro IS eRecept SÚKL
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í.
Soubory
| Soubor | Co dělá |
|---|---|
NacistPredpis_FUNKCNI.py |
Stáhne detail jednoho receptu dle hardcoded ID_Dokladu (ruční test) |
08StahnoutPredpisy.py |
Starší skript bez DB integrace — nahrazen 10_StahnoutXML.py |
09_VytvorTabulky.py |
Vytvoří tabulky recept_doklad a recept_plp v MySQL |
10_StahnoutXML.py |
Stahování — načte ERP kódy z Medicusu, přeskočí terminální, uloží XML |
11_ParseXML.py |
Parsování — naparsuje XML archiv a uloží data do MySQL |
NačteníPředpisuWithClaude/
├── NacistPredpis_FUNKCNI.py ← test jednoho receptu
├── 08StahnoutPredpisy.py ← starší skript (bez DB)
├── 09_VytvorTabulky.py ← DDL MySQL tabulek
├── 10_StahnoutXML.py ← hromadné stahování s přeskakováním
├── 11_ParseXML.py ← parsování XML do MySQL
├── 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
Co NacistPredpis vrací navíc oproti NacistLekovyZaznam
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.
NacistPredpis (endpoint /cuer/Lekar, namespace 201704) vrací detail
jednoho konkrétního receptu, včetně:
Údaje o receptu (dokladu)
| 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 / PACIENT |
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 / F (ne M/Z jak uvádí XSD — reálně posíláno M/F) |
Telefon |
Telefonní číslo |
Notifikace |
SMS / Email |
Údaje o předepisujícím
| Pole | Popis |
|---|---|
Lekar.Kod |
UUID lékaře — nebo "skryto" (ukládáme jako NULL) |
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í operací
| NacistPredpis | NacistLekovyZaznam | |
|---|---|---|
| Namespace | 201704 |
201912 |
| Endpoint | /cuer/Lekar |
/cuer/Lekar2 |
| SOAPAction | NacistPredpis |
NacistLekovyZaznam |
| 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.5–4.5 KB | ~227 KB |
| Pokrytí | pouze naše ordinace (ERP kód z Medicusu) | všichni lékaři pacienta |
Autentizace (stejná jako u všech operací eReceptu)
| 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 |
Certifikát se hledá relativně ke skriptu: ../../AMBSUKL214235369G_31DEC2024.pfx
Zdroj ID_Dokladu — Medicus (Firebird)
Alfanumerický kód receptu (ID_Dokladu) není v hromadném lékovém záznamu.
Nachází se v tabulce RECEPT_EPODANI v Medicusu:
RECEPT.id_epodani → RECEPT_EPODANI.id
RECEPT_EPODANI.erp = ID_Dokladu (např. "PPIBVF93285E")
Důsledek: Detaily receptů lze stáhnout pouze pro naši ordinaci. O předpisech cizích lékařů víme jen to, co vrací lékový záznam.
SQL dotaz
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
Pozor:
LIMITv10_StahnoutXML.pyomezuje počet řádků z Firebirdu před deduplikací. Po deduplikaci (jeden recept = více léků = více řádků) může být výsledný počet receptů nižší.
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é)
Databázové schéma — MySQL
Relační diagram
recept_doklad (1) ────────────────── (N) recept_plp
id_dokladu PK id_lp PK ──────► predpis.id_lp_predpis
id_dokladu FK
Tabulka recept_plp.id_lp = predpis.id_lp_predpis — přímý JOIN s lékovým záznamem.
Tabulka recept_doklad
Jeden řádek na celý recept (ID_Dokladu).
| Sloupec | Typ | Poznámka |
|---|---|---|
id_dokladu |
VARCHAR(20) PK | ERP kód |
stav |
ENUM | PREDEPSANY / PRIPRAVOVANY / CASTECNE_VYDANY / PLNE_VYDANY / ZRUSENY |
stav_terminal |
TINYINT(1) | 1 = nepotřebuje další stahování |
datum_vystaveni |
DATE | |
platnost_do |
DATE | |
vypis_do |
DATE | prodloužení výpisem |
akutni |
TINYINT(1) | |
rodina |
TINYINT(1) | ad usum proprium |
opakovani |
INT | NULL = není opakovací |
druh_pojisteni |
ENUM | VEREJNE / OSTATNI / POJISTENI_EU |
modry_pruh |
TINYINT(1) | |
pozn |
VARCHAR(1000) | |
zap_doplatek |
DECIMAL(10,2) | ZapocitatelnyDoplatekZbyvaDoLimitu |
zalozeni / zmena |
DATETIME | z eReceptu |
lekar_kod |
CHAR(36) | UUID lékaře; NULL pokud "skryto" |
odbornost_kod / odbornost_nazev |
VARCHAR | |
lekar_email |
VARCHAR(100) | |
cp |
VARCHAR(10) | číslo pojištěnce |
zp_kod / zp_nazev |
VARCHAR | pojišťovna při předpisu (snapshot) |
pac_telefon |
VARCHAR(20) | |
pac_notifikace |
ENUM | SMS / EMAIL |
pac_pohlavi |
VARCHAR(5) | M / F |
xml_soubor |
VARCHAR(255) | cesta k poslednímu XML |
stazeno |
DATETIME | poslední aktualizace |
Tabulka recept_plp
Jeden řádek na PLP položku (lék na receptu).
| Sloupec | Typ | Poznámka |
|---|---|---|
id_lp |
CHAR(36) PK | UUID = predpis.id_lp_predpis |
id_dokladu |
VARCHAR(20) FK | |
uhrada |
VARCHAR(20) | ZAKLADNI / ZVYSENA / NEHRAZENY / PACIENT |
prekroceni |
TINYINT(1) |
JOIN lékový záznam + detail receptu
SELECT p.datum_vystaveni, p.nazev, p.atc, p.navod,
rd.stav, rd.platnost_do, rd.zp_nazev,
rp.uhrada,
v.datum_vydeje
FROM predpis p
LEFT JOIN recept_plp rp ON rp.id_lp = p.id_lp_predpis
LEFT JOIN recept_doklad rd ON rd.id_dokladu = rp.id_dokladu
LEFT JOIN vydej v ON v.id_lp_predpis = p.id_lp_predpis
WHERE p.atc LIKE 'C09%'
ORDER BY p.datum_vystaveni DESC;
LEFT JOIN — pro cizí lékaře
rdarpbudou NULL (nemáme jejich ERP kód).
10_StahnoutXML.py — stahování
Parametry (editovat přímo v souboru)
DATUM_OD = "2025-01-01" # recepty od tohoto data
LIMIT = None # max počet receptů; None = bez omezení
Logika přeskakování
Na začátku načte z MySQL jeden dotaz:
SELECT id_dokladu FROM recept_doklad WHERE stav_terminal = 1
Výsledek = Python set. Pro každý ERP kód z Medicusu:
- je v setu → přeskočit (vydaný / zrušený / expirovaný)
- není v setu → stáhnout
Stornované recepty jsou filtrovány přímo v dotazu do Firebirdu (AND r.STORNO = 'F'),
takže se pro ně SOAP volání na SÚKL vůbec neprovádí.
Co je terminální (stav_terminal = 1)
stav IN ('PLNE_VYDANY', 'ZRUSENY')- nebo
platnost_do < dnes(expirovaný bez vyzvednutí)
Ošetření chyb
| Kód | Popis | Chování |
|---|---|---|
| D003 | Předpis zrušen lékařem | Uloží _CHYBA.xml, pokračuje |
| HTTP 500 | SOAP Fault | Uloží _CHYBA.xml, pokračuje |
| Exception | Síťová chyba | Vypíše EXCEPTION, pokračuje |
11_ParseXML.py — parsování do MySQL
Parametry (editovat přímo v souboru)
DATUM_FILTR = None # např. "2026-04-14", nebo None = celý archiv
Co dělá
- Načte z MySQL
{id_dokladu: xml_soubor}pro všechny již zpracované záznamy - Najde nejnovější XML pro každý ERP kód (nejvyšší datum v adresářové struktuře)
- Přeskočí soubory, jejichž cesta se nezměnila oproti DB — zpracuje jen nově stažené
- Naparsuje stav, platnost, pacient, předepisující, PLP položky
recept_doklad: INSERT ... ON DUPLICATE KEY UPDATE (stav se může změnit)recept_plp: INSERT IGNORE (UUID je stabilní)
Správné pořadí spuštění
10_StahnoutXML.py → 11_ParseXML.py → 10_StahnoutXML.py → ...
10 se spoléhá na stav_terminal v MySQL, který nastavuje 11.
Bez spuštění 11 budou terminální recepty znovu stahovány.
Ověřeno (16. 4. 2026)
- Hromadné stažení od 1. 1. 2025 dokončeno (
LIMIT = None) - Staženo 9 616 receptů (po filtraci stornovaných z Firebirdu)
11_ParseXML.pypřeskakuje nezměněné soubory — opakované spuštění zpracuje jen nově stažené
XSD zdroje
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 |