Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ověření pojistného statusu pacientů (VZP B2B)
Systém pro automatické ověřování, zda jsou registrovaní pacienti platně pojištěni u zdravotní pojišťovny. Dotazuje VZP B2B API, ukládá historii do MySQL a generuje PDF reporty o neshodách.
Architektura
Medicus Firebird DB (192.168.1.4)
└─ get_active_registered_patients()
│
▼
VZPB2BClient.stav_pojisteni() ←─ mTLS (PFX certifikát)
prod.b2b.vzp.cz/stavPojisteniB2B
│
▼
MySQL medevio.vzp_stav_pojisteni
│
▼
Reportovací skript
└─ binární hledání data zlomu pojištění
└─ Jinja2 + WeasyPrint → PDF
Soubory
20 Ověření proti Medicus/
├── 10 FinalSaveInsuranceStatusScript(R).py ← sběr dat (spouštět denně)
├── 10 Ověření proti medicus.py ← generování PDF reportu
├── insurance_check.log ← log sběru dat
└── Output/
└── kontrola_pojisteni_YYYY-MM-DD_HH-MM-SS.pdf
Sdílené knihovny (knihovny/)
| Soubor | Třída | Popis |
|---|---|---|
medicus_db.py |
MedicusDB |
Wrapper pro Firebird připojení k Medicus |
vzpb2b_client.py |
VZPB2BClient |
VZP B2B SOAP API klient s mTLS |
Skript 1: FinalSaveInsuranceStatusScript
Soubor: 10 FinalSaveInsuranceStatusScript(R).py
Inkrementální sběr pojistných stavů. Lze spouštět opakovaně — při druhém spuštění ve stejný den přeskočí již zkontrolované pacienty.
Postup
- Načte všechny aktivně registrované pacienty z Medicus (
registr.priznak IN ('A','D','V'),kar.vyrazen <> 'A') - Pro každého pacienta zkontroluje
MAX(k_datu)v MySQL — pokud je dnešní, přeskočí ho - Zavolá VZP B2B
stavPojisteniB2BSOAP endpoint - Uloží XML odpověď + parsovaná data do tabulky
vzp_stav_pojisteni - Mezi dotazy čeká 1,4 s (ochrana proti přetížení VZP API)
Konfigurace
| Proměnná | Hodnota | Popis |
|---|---|---|
HOST |
192.168.1.4 |
Medicus Firebird server |
DB_PATH |
c:\Medicus 3\data\MEDICUS.FDB |
Cesta k Firebird databázi |
PFX_PATH |
certificates/picka.pfx |
Klientský certifikát pro mTLS |
PFX_PASSWORD |
— | Heslo k PFX souboru |
ENV |
prod |
Prostředí VZP (prod / simu) |
| MySQL host | 192.168.1.76:3306 |
MySQL server, databáze medevio |
Logování
Výstup jde současně do insurance_check.log i na konzoli. Formát:
2026-01-26 08:15:23 [INFO] Loaded 1620 registered patients
2026-01-26 08:15:23 [INFO] Incremental run: 1620 patients to check today
2026-01-26 08:15:25 [INFO] [1/1620] Checking Novák Jan (1234567890)
2026-01-26 08:15:26 [INFO] ✔ OK (VZP)
Skript 2: Ověření proti medicus (report)
Soubor: 10 Ověření proti medicus.py
Porovná registrované pacienty v Medicus s posledním zaznamenaným pojistným stavem v MySQL. Pacienty s neaktivním pojištěním (stav != "1") vypíše do PDF reportu včetně přesného data zlomu pojištění.
Postup
- Načte registrované pacienty z Medicus
- Z MySQL vezme poslední stav pojištění pro každé RC (pomocí
ROW_NUMBER() OVER PARTITION BY rc ORDER BY k_datu DESC) - Vybere "podezřelé" — registrovaní v Medicus, ale stav
!= "1" - Pro každého podezřelého provede binární hledání data zlomu:
- hledá mezi
MAX(k_datu WHERE stav='1')a dneškem - výsledek:
insured_to(poslední den pojištění) auninsured_from(první den bez pojištění)
- hledá mezi
- Vygeneruje HTML přes Jinja2 šablonu a převede na PDF přes WeasyPrint
- PDF otevře automaticky v systémovém prohlížeči
Šablona a výstup
- Šablona:
Templates/vzp_console_report.html - Fonty:
Templates/fonts/DejaVuSans*.ttf - Výstup:
Output/kontrola_pojisteni_YYYY-MM-DD_HH-MM-SS.pdf
Databáze
Tabulka vzp_stav_pojisteni (MySQL, medevio)
| Sloupec | Typ | Popis |
|---|---|---|
id |
INT AUTO_INCREMENT | Primární klíč |
rc |
VARCHAR(20) | Rodné číslo pacienta |
prijmeni |
VARCHAR(100) | Příjmení |
jmeno |
VARCHAR(100) | Jméno |
k_datu |
DATE | Datum, ke kterému byl stav zjišťován |
stav |
VARCHAR(10) | "1" = pojištěn, ostatní = problém |
kod_pojistovny |
VARCHAR(10) | Kód pojišťovny dle VZP odpovědi |
nazev_pojistovny |
VARCHAR(100) | Název pojišťovny |
pojisteni_kod |
VARCHAR(20) | Kód druhu pojištění |
stav_vyrizeni |
INT | stavVyrizeniPozadavku ze SOAP odpovědi |
response_xml |
MEDIUMTEXT | Celá SOAP XML odpověď od VZP |
created_at |
TIMESTAMP | Čas vložení záznamu |
Indexy: idx_rc (rc), idx_rc_k_datu (rc, k_datu), idx_rc_stav (rc, stav)
Klíčové SQL dotazy skriptů
-- Inkrementální filtr (skript 1)
SELECT MAX(k_datu) AS last_check FROM vzp_stav_pojisteni WHERE rc = ?
-- Poslední stav každého pacienta (skript 2)
SELECT rc, stav FROM (
SELECT rc, stav,
ROW_NUMBER() OVER (PARTITION BY rc ORDER BY k_datu DESC) AS rn
FROM vzp_stav_pojisteni
) t WHERE rn = 1
-- Poslední den aktivního pojištění (pro binární hledání)
SELECT MAX(k_datu) AS last_insured
FROM vzp_stav_pojisteni WHERE rc = ? AND stav = '1'
VZP B2B API
Třída VZPB2BClient
Autentizace: mTLS — klientský PFX certifikát přes requests_pkcs12.Pkcs12Adapter
Metody:
| Metoda | SOAP služba | Popis |
|---|---|---|
stav_pojisteni(rc, k_datu) |
stavPojisteniB2B |
Ověří pojistný stav pacienta k datu |
parse_stav_pojisteni(xml) |
— | Parsuje XML odpověď na dict |
over_prukaz_pojistence(cislo, k_datu) |
OverPrukazPojistenceB2B |
Ověří průkaz pojištěnce (EHIC) |
Prostředí:
| ENV | URL |
|---|---|
prod |
https://prod.b2b.vzp.cz/B2BProxy/HttpProxy/stavPojisteniB2B |
simu |
https://simu.b2b.vzp.cz/B2BProxy/HttpProxy/SIMUstavPojisteniB2B |
Návratová struktura parse_stav_pojisteni():
{
"stavVyrizeni": int, # stavVyrizeniPozadavku ze SOAP
"stav": str, # "1" = pojištěn
"kodPojistovny": str, # např. "111"
"nazevPojistovny": str, # např. "VZP"
"pojisteniKod": str
}
Medicus DB
Třída MedicusDB
Připojení: Firebird (fdb), 192.168.1.4, charset WIN1250, user SYSDBA.
Metoda get_active_registered_patients() — vrací pacienty splňující:
registr.datum_zruseni IS NULL— registrace není zrušenáregistr.priznak IN ('A','D','V')— aktivní typy registracekar.rodcis IS NOT NULL AND kar.rodcis <> ''— má rodné číslokar.vyrazen <> 'A'— pacient není vyřazen
Kapacitní odhad
| Parametr | Hodnota |
|---|---|
| Počet pacientů | ~1 600 |
| Velikost XML odpovědi | ~500–1 500 B |
| Přírůstek dat | ~1,6 MB/den |
| Přírůstek řádků | ~1 600/den |
| Roční objem | ~580 MB / ~580 000 řádků |
| Limit MEDIUMTEXT | 16 MB/řádek — bez problémů |
Spuštění
# 1. Denní sběr dat (ideálně naplánovat přes Task Scheduler)
python "10 FinalSaveInsuranceStatusScript(R).py"
# 2. Vygenerování PDF reportu (na vyžádání)
python "10 Ověření proti medicus.py"