notebook vb
This commit is contained in:
Submodule
+1
Submodule .claude/worktrees/vigorous-davinci added at 45a1642df8
@@ -0,0 +1,51 @@
|
|||||||
|
# Medicus + Claude – kontext projektu
|
||||||
|
|
||||||
|
## Přečti si prosím tyto soubory na začátku každé konverzace
|
||||||
|
|
||||||
|
1. `MedicusWithClaude/CLAUDE_NOTES.md` – hlavní poznámky: DB připojení, klíčové tabulky, RTF formát, import pipeline
|
||||||
|
2. `MedicusWithClaudeSelects/SELECTS.md` – SQL dotazy (registrovaní pacienti atd.)
|
||||||
|
3. `MedicusWithClaudeSelects/FakturaceADavky.md` – tabulky FAK, FAKDET, FAKDAV, PORTAL, kódování dávek
|
||||||
|
|
||||||
|
## O projektu
|
||||||
|
|
||||||
|
Firebird DB lékařského SW **Medicus** pro praktického lékaře. Lékařka je **MUDr. Buzalková Michaela** (IDUZI=4). Vladimír Buzalka (IDUZI=6) je manžel a správce systému – s ním probíhají tyto konverzace.
|
||||||
|
|
||||||
|
## Připojení k DB
|
||||||
|
|
||||||
|
```python
|
||||||
|
import fdb
|
||||||
|
conn = fdb.connect(
|
||||||
|
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
|
||||||
|
user='SYSDBA', password='masterkey', charset='win1250'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Počítač "reporter"
|
||||||
|
|
||||||
|
Na tomto stroji běží pravidelná automatická tvorba reportů. Připojení k DB funguje stejně jako výše (ostrá DB, čerstvá data). Hlavní report skript: `MedicusWithClaudeFaktury/faktury_report.py`
|
||||||
|
|
||||||
|
### Co report dělá
|
||||||
|
- Generuje Excel s listy: **FAK**, **FAKDET**, **PORTAL**, **PORTAL_DATA**
|
||||||
|
- Ukládá do `u:\Dropbox\!!!Days\Downloads Z230\`
|
||||||
|
- Název souboru: `YYYY-MM-DD_HH-MM-SS_faktury.xlsx`
|
||||||
|
- Maže předchozí verze ze stejné složky
|
||||||
|
- Řazení: nejnovější záznamy nahoře
|
||||||
|
- Hyperlinky: FAK → FAKDET, PORTAL ↔ PORTAL_DATA
|
||||||
|
|
||||||
|
### Důležité – kódování dávek
|
||||||
|
KDAVKA/FDAVKA jsou v **CP852** (DOS). fdb je vrací jako string dekódovaný win1250 – nutno re-enkódovat:
|
||||||
|
```python
|
||||||
|
spravny_text = s.encode('cp1250', errors='replace').decode('cp852', errors='replace')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Klíčové adresáře
|
||||||
|
|
||||||
|
| Adresář | Obsah |
|
||||||
|
|---|---|
|
||||||
|
| `MedicusWithClaude/` | průzkumné skripty, import pipeline (s03soubory.py) |
|
||||||
|
| `MedicusWithClaudeSelects/` | SQL dotazy, dokumentace tabulek |
|
||||||
|
| `MedicusWithClaudeFaktury/` | fakturační reporty |
|
||||||
|
|
||||||
|
## Co chceme na reporteru nastavit
|
||||||
|
|
||||||
|
Pravidelná automatická tvorba reportu `faktury_report.py` – např. každý den ráno, aby byl vždy čerstvý Excel s fakturami v Dropboxu.
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
import fdb
|
||||||
|
import openpyxl
|
||||||
|
from openpyxl.styles import Font, PatternFill, Alignment
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
from datetime import datetime
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# --- Připojení ---
|
||||||
|
conn = fdb.connect(
|
||||||
|
dsn=r'localhost:c:\medicus 3\data\medicus.fdb',
|
||||||
|
user='SYSDBA', password='masterkey', charset='win1250'
|
||||||
|
)
|
||||||
|
cur = conn.cursor()
|
||||||
|
|
||||||
|
# --- Výstupní soubor ---
|
||||||
|
output_dir = r'u:\Dropbox\!!!Days\Downloads Z230'
|
||||||
|
now = datetime.now()
|
||||||
|
filename = now.strftime('%Y-%m-%d_%H-%M-%S') + '_faktury.xlsx'
|
||||||
|
output_path = os.path.join(output_dir, filename)
|
||||||
|
|
||||||
|
# --- Smazání předchozích verzí ---
|
||||||
|
for f in os.listdir(output_dir):
|
||||||
|
if f.endswith('_faktury.xlsx'):
|
||||||
|
os.remove(os.path.join(output_dir, f))
|
||||||
|
|
||||||
|
wb = openpyxl.Workbook()
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# Pomocné funkce
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
HEADER_FILL = PatternFill('solid', fgColor='2F5496')
|
||||||
|
HEADER_FONT = Font(bold=True, color='FFFFFF')
|
||||||
|
LINK_FONT = Font(color='0563C1', underline='single')
|
||||||
|
ZEBRA_FILL = PatternFill('solid', fgColor='DCE6F1')
|
||||||
|
|
||||||
|
def style_header(ws):
|
||||||
|
for cell in ws[1]:
|
||||||
|
cell.fill = HEADER_FILL
|
||||||
|
cell.font = HEADER_FONT
|
||||||
|
cell.alignment = Alignment(horizontal='center')
|
||||||
|
|
||||||
|
def autofit(ws):
|
||||||
|
for col in ws.columns:
|
||||||
|
max_len = max((len(str(cell.value)) if cell.value is not None else 0) for cell in col)
|
||||||
|
ws.column_dimensions[get_column_letter(col[0].column)].width = min(max_len + 2, 40)
|
||||||
|
|
||||||
|
def fmt(val):
|
||||||
|
if val is None:
|
||||||
|
return ''
|
||||||
|
return val
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# List 1 – FAK
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
ws1 = wb.active
|
||||||
|
ws1.title = 'FAK'
|
||||||
|
|
||||||
|
cur.execute('''
|
||||||
|
SELECT
|
||||||
|
ID, CISFAK, POJ, DATUMOD, DATUMDO, DATVYS, DATODE,
|
||||||
|
VYKONY, KAPITACE, ZALOHA, CENA, ZAPLACENO, ZUM, HOSPAUSAL,
|
||||||
|
PROPLACENO, SPLAT, DRUH, TYP, ROK, OBDOB,
|
||||||
|
NAZFAK, POZFAK, OBDFAK,
|
||||||
|
ICO, BANKA, UCET,
|
||||||
|
ODJMENO, ODULICE, ODMISTO, ODPSC,
|
||||||
|
PLNAZEV, PLULICE, PLMISTO, PLPSC,
|
||||||
|
ICZ, ICZ1, IDICZ, PORCISLO, DRUHPOJ, POZNAMKA
|
||||||
|
FROM FAK
|
||||||
|
ORDER BY ID DESC
|
||||||
|
''')
|
||||||
|
fak_cols = [d[0] for d in cur.description]
|
||||||
|
fak_rows = cur.fetchall()
|
||||||
|
|
||||||
|
ws1.append(fak_cols)
|
||||||
|
for i, row in enumerate(fak_rows, start=2):
|
||||||
|
ws1.append([fmt(v) for v in row])
|
||||||
|
if i % 2 == 0:
|
||||||
|
for cell in ws1[i]:
|
||||||
|
cell.fill = ZEBRA_FILL
|
||||||
|
|
||||||
|
style_header(ws1)
|
||||||
|
ws1.freeze_panes = 'A2'
|
||||||
|
autofit(ws1)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# List 2 – FAKDET
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
ws2 = wb.create_sheet('FAKDET')
|
||||||
|
|
||||||
|
cur.execute('''
|
||||||
|
SELECT
|
||||||
|
fd.ID, fd.IDFAK,
|
||||||
|
f.CISFAK, f.POJ, f.DATUMOD, f.DATUMDO, f.ROK,
|
||||||
|
fd.ICP, fd.ODB, fd.IDUZI,
|
||||||
|
u.PRIJMENI, u.JMENO,
|
||||||
|
fd.CENAVYK, fd.CENALEC, fd.CENAKAP
|
||||||
|
FROM FAKDET fd
|
||||||
|
JOIN FAK f ON f.ID = fd.IDFAK
|
||||||
|
LEFT JOIN UZIVATEL u ON u.IDUZI = fd.IDUZI
|
||||||
|
ORDER BY fd.IDFAK DESC, fd.ID DESC
|
||||||
|
''')
|
||||||
|
det_cols = [d[0] for d in cur.description]
|
||||||
|
det_rows = cur.fetchall()
|
||||||
|
|
||||||
|
ws2.append(det_cols)
|
||||||
|
for i, row in enumerate(det_rows, start=2):
|
||||||
|
ws2.append([fmt(v) for v in row])
|
||||||
|
if i % 2 == 0:
|
||||||
|
for cell in ws2[i]:
|
||||||
|
cell.fill = ZEBRA_FILL
|
||||||
|
|
||||||
|
style_header(ws2)
|
||||||
|
ws2.freeze_panes = 'A2'
|
||||||
|
autofit(ws2)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# List 3 – PORTAL (krátké sloupce)
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
ws3 = wb.create_sheet('PORTAL')
|
||||||
|
|
||||||
|
cur.execute('''
|
||||||
|
SELECT ID, IDFAK, ODESLANO, CHYBA, STAV, ID_PODANI, IDPODANI, IDCERT,
|
||||||
|
DAVKA_ROK, DAVKA_DISK, DAVKA_IDICZ, DAVKA_DATUMOD, DAVKA_DATUMDO,
|
||||||
|
DAVKA_CASTKA, BB_DAVKA, BB_FAKTURA
|
||||||
|
FROM PORTAL
|
||||||
|
ORDER BY ID DESC
|
||||||
|
''')
|
||||||
|
portal_cols = [d[0] for d in cur.description]
|
||||||
|
portal_rows = cur.fetchall()
|
||||||
|
|
||||||
|
ws3.append(portal_cols)
|
||||||
|
for i, row in enumerate(portal_rows, start=2):
|
||||||
|
ws3.append([fmt(v) for v in row])
|
||||||
|
if i % 2 == 0:
|
||||||
|
for cell in ws3[i]:
|
||||||
|
cell.fill = ZEBRA_FILL
|
||||||
|
|
||||||
|
style_header(ws3)
|
||||||
|
ws3.freeze_panes = 'A2'
|
||||||
|
autofit(ws3)
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# List 4 – PORTAL_DATA (BLOBy)
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
ws4 = wb.create_sheet('PORTAL_DATA')
|
||||||
|
|
||||||
|
cur.execute('''
|
||||||
|
SELECT ID, ID_PODANI, DAVKA_ROK, DAVKA_DISK, DAVKA_DATUMOD, DAVKA_DATUMDO,
|
||||||
|
KDAVKA, FDAVKA, DATA
|
||||||
|
FROM PORTAL
|
||||||
|
ORDER BY ID DESC
|
||||||
|
''')
|
||||||
|
pdata_rows = cur.fetchall()
|
||||||
|
|
||||||
|
pdata_cols = ['ID', 'ID_PODANI', 'DAVKA_ROK', 'DAVKA_DISK', 'DAVKA_DATUMOD', 'DAVKA_DATUMDO',
|
||||||
|
'KDAVKA', 'FDAVKA', 'DATA']
|
||||||
|
ws4.append(pdata_cols)
|
||||||
|
|
||||||
|
WRAP = Alignment(wrap_text=True, vertical='top')
|
||||||
|
|
||||||
|
# Indexy BLOB sloupců: KDAVKA=6, FDAVKA=7, DATA=8
|
||||||
|
# DATA je XML v win1250, KDAVKA/FDAVKA jsou latin2
|
||||||
|
BLOB_ENCODINGS = {6: 'cp852', 7: 'cp852', 8: 'cp1250'}
|
||||||
|
|
||||||
|
def decode_blob(v, enc):
|
||||||
|
if hasattr(v, 'read'):
|
||||||
|
raw = v.read()
|
||||||
|
else:
|
||||||
|
raw = v
|
||||||
|
if not raw:
|
||||||
|
return ''
|
||||||
|
if isinstance(raw, str):
|
||||||
|
# fdb dekódoval jako win1250 – vrátíme zpět na bytes, pak dekódujeme správně
|
||||||
|
raw = raw.encode('cp1250', errors='replace')
|
||||||
|
return raw.decode(enc, errors='replace')
|
||||||
|
|
||||||
|
for i, row in enumerate(pdata_rows, start=2):
|
||||||
|
out = []
|
||||||
|
for col_idx, v in enumerate(row):
|
||||||
|
if col_idx in BLOB_ENCODINGS:
|
||||||
|
enc = BLOB_ENCODINGS[col_idx]
|
||||||
|
out.append(decode_blob(v, enc))
|
||||||
|
else:
|
||||||
|
out.append(fmt(v))
|
||||||
|
ws4.append(out)
|
||||||
|
if i % 2 == 0:
|
||||||
|
for cell in ws4[i]:
|
||||||
|
cell.fill = ZEBRA_FILL
|
||||||
|
for cell in ws4[i]:
|
||||||
|
cell.alignment = WRAP
|
||||||
|
ws4.row_dimensions[i].height = 80
|
||||||
|
|
||||||
|
style_header(ws4)
|
||||||
|
ws4.freeze_panes = 'A2'
|
||||||
|
|
||||||
|
# Šířky sloupců PORTAL_DATA
|
||||||
|
for col, width in zip(['A','B','C','D','E','F','G','H','I'], [6, 26, 8, 6, 12, 12, 80, 20, 60]):
|
||||||
|
ws4.column_dimensions[col].width = width
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# Hyperlinky PORTAL ↔ PORTAL_DATA
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
# Mapa: portal_id → řádek v každém listu
|
||||||
|
portal_id_to_ws3_row = {}
|
||||||
|
for i, row in enumerate(portal_rows, start=2):
|
||||||
|
portal_id_to_ws3_row[row[0]] = i # row[0] = ID
|
||||||
|
|
||||||
|
portal_id_to_ws4_row = {}
|
||||||
|
for i, row in enumerate(pdata_rows, start=2):
|
||||||
|
portal_id_to_ws4_row[row[0]] = i # row[0] = ID
|
||||||
|
|
||||||
|
# PORTAL → PORTAL_DATA (sloupec A = ID)
|
||||||
|
for i, row in enumerate(portal_rows, start=2):
|
||||||
|
pid = row[0]
|
||||||
|
cell = ws3.cell(row=i, column=1)
|
||||||
|
if pid in portal_id_to_ws4_row:
|
||||||
|
cell.hyperlink = f'#PORTAL_DATA!A{portal_id_to_ws4_row[pid]}'
|
||||||
|
cell.font = LINK_FONT
|
||||||
|
|
||||||
|
# PORTAL_DATA → PORTAL (sloupec A = ID)
|
||||||
|
for i, row in enumerate(pdata_rows, start=2):
|
||||||
|
pid = row[0]
|
||||||
|
cell = ws4.cell(row=i, column=1)
|
||||||
|
if pid in portal_id_to_ws3_row:
|
||||||
|
cell.hyperlink = f'#PORTAL!A{portal_id_to_ws3_row[pid]}'
|
||||||
|
cell.font = LINK_FONT
|
||||||
|
cell.alignment = WRAP
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# Hyperlinky FAK → FAKDET
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
# Mapa: IDFAK → první řádek na listu FAKDET (řádek 1 = záhlaví, data od 2)
|
||||||
|
idfak_to_row = {}
|
||||||
|
for i, row in enumerate(det_rows, start=2):
|
||||||
|
idfak = row[1] # IDFAK je druhý sloupec
|
||||||
|
if idfak not in idfak_to_row:
|
||||||
|
idfak_to_row[idfak] = i
|
||||||
|
|
||||||
|
# Přidat sloupec "→FAKDET" jako první sloupec na listu FAK
|
||||||
|
ws1.insert_cols(1)
|
||||||
|
ws1.cell(row=1, column=1, value='FAKDET').fill = HEADER_FILL
|
||||||
|
ws1.cell(row=1, column=1).font = HEADER_FONT
|
||||||
|
ws1.cell(row=1, column=1).alignment = Alignment(horizontal='center')
|
||||||
|
ws1.column_dimensions['A'].width = 9
|
||||||
|
|
||||||
|
for i, row in enumerate(fak_rows, start=2):
|
||||||
|
fak_id = row[0] # ID je první sloupec z DB
|
||||||
|
cell = ws1.cell(row=i, column=1)
|
||||||
|
if fak_id in idfak_to_row:
|
||||||
|
target_row = idfak_to_row[fak_id]
|
||||||
|
cell.value = '>> det'
|
||||||
|
cell.hyperlink = f'#FAKDET!A{target_row}'
|
||||||
|
cell.font = LINK_FONT
|
||||||
|
else:
|
||||||
|
cell.value = ''
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# Uložení
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
wb.save(output_path)
|
||||||
|
sys.stdout.buffer.write(f'Ulozeno: {output_path}\n'.encode('utf-8'))
|
||||||
|
sys.stdout.buffer.write(f'FAK: {len(fak_rows)} radku, FAKDET: {len(det_rows)} radku, PORTAL: {len(portal_rows)} radku\n'.encode('utf-8'))
|
||||||
@@ -0,0 +1,252 @@
|
|||||||
|
# Fakturace a dávky – poznámky pro Clauda
|
||||||
|
|
||||||
|
## Přehled tabulek
|
||||||
|
|
||||||
|
### FAK – faktury pojišťovnám
|
||||||
|
- **844 záznamů** (k 2026-03-28)
|
||||||
|
- Primární klíč: `ID`
|
||||||
|
|
||||||
|
| Sloupec | Popis |
|
||||||
|
|---|---|
|
||||||
|
| `ID` | primární klíč |
|
||||||
|
| `CISFAK` | číslo faktury (10 znaků) |
|
||||||
|
| `POJ` | pojišťovna (3 znaky, např. 111=VZP, 205=ČPZP, 207=OZP) |
|
||||||
|
| `ICZ`, `ICZ1` | IČZ ordinace |
|
||||||
|
| `IDICZ` | FK → ICZ.IDICZ (jen jedna ordinace, neřešit) |
|
||||||
|
| `PORCISLO` | pořadové číslo |
|
||||||
|
| `DATUMOD`, `DATUMDO` | období faktury (od–do) |
|
||||||
|
| `DATVYS` | datum vystavení |
|
||||||
|
| `DATODE` | datum odeslání |
|
||||||
|
| `OBDOB` | období (5 znaků, např. "20260") |
|
||||||
|
| `VYKONY` | částka za výkony (Kč) |
|
||||||
|
| `KAPITACE` | kapitační platba (Kč) |
|
||||||
|
| `ZALOHA` | záloha |
|
||||||
|
| `CENA` | celková cena faktury (Kč) |
|
||||||
|
| `DRUH` | druh faktury (např. "konečná") |
|
||||||
|
| `TYP` | typ (1 znak) |
|
||||||
|
| `ROK` | rok |
|
||||||
|
| `SPLAT` | datum splatnosti |
|
||||||
|
| `PROPLACENO` | datum proplacení (NULL = nezaplaceno) |
|
||||||
|
| `ZAPLACENO` | zaplacená částka |
|
||||||
|
| `ZUM` | ? |
|
||||||
|
| `HOSPAUSAL` | hospitalizační/paušální? |
|
||||||
|
| `FDAVKA` | BLOB – odkaz na dávku |
|
||||||
|
| `KAPDETAIL`, `AGRDETAIL` | BLOBy – detaily kapitace/agregace |
|
||||||
|
| `NAZFAK`, `POZFAK`, `OBDFAK` | název, poznámka, období faktury (texty) |
|
||||||
|
| `ICO`, `BANKA`, `UCET` | IČO, banka, účet |
|
||||||
|
| `ODJMENO/ULICE/MISTO/PSC` | adresa odesílatele |
|
||||||
|
| `PLNAZEV/ULICE/MISTO/PSC` | adresa plátce (pojišťovny) |
|
||||||
|
| `DRUHPOJ` | druh pojištění |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### FAKDET – finanční detail faktury
|
||||||
|
- **1021 záznamů**
|
||||||
|
- Vazba: `IDFAK` → FAK.ID
|
||||||
|
- Typicky 1–2 řádky na fakturu (breakdown podle lékaře/IDUZI)
|
||||||
|
|
||||||
|
| Sloupec | Popis |
|
||||||
|
|---|---|
|
||||||
|
| `ID` | primární klíč |
|
||||||
|
| `IDFAK` | FK → FAK.ID |
|
||||||
|
| `ICP` | IČP ordinace (např. 09305001) |
|
||||||
|
| `ODB` | odbornost (001 = praktický lékař) |
|
||||||
|
| `IDUZI` | FK → UZIVATEL.IDUZI – který lékař |
|
||||||
|
| `CENAVYK` | výkony (Kč) |
|
||||||
|
| `CENALEC` | léky (Kč) |
|
||||||
|
| `CENAKAP` | kapitace (Kč) |
|
||||||
|
|
||||||
|
**Poznámka:** IDUZI=0 = systémový záznam (kapitace bez konkrétního lékaře).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### FAKDAV – dávky zahrnuté ve faktuře
|
||||||
|
- **1296 záznamů**
|
||||||
|
- Vazba: `IDFAK` → FAK.ID
|
||||||
|
|
||||||
|
| Sloupec | Popis |
|
||||||
|
|---|---|
|
||||||
|
| `ID` | primární klíč |
|
||||||
|
| `IDFAK` | FK → FAK.ID |
|
||||||
|
| `CISDAV` | číslo dávky |
|
||||||
|
| `DRUH` | druh dávky |
|
||||||
|
| `CENA` | celková cena dávky |
|
||||||
|
| `CENAVYK` | výkony |
|
||||||
|
| `CENALEC` | léky |
|
||||||
|
| `CENAPAU` | paušál |
|
||||||
|
| `ODB` | odbornost |
|
||||||
|
| `ICP` | IČP |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### PORTAL – elektronické podání pojišťovně
|
||||||
|
- **180 záznamů**
|
||||||
|
- Vazba: `IDFAK` → FAK.ID
|
||||||
|
|
||||||
|
| Sloupec | Popis |
|
||||||
|
|---|---|
|
||||||
|
| `ID` | primární klíč |
|
||||||
|
| `IDFAK` | FK → FAK.ID |
|
||||||
|
| `ODESLANO` | timestamp odeslání |
|
||||||
|
| `DATA`, `KDAVKA`, `FDAVKA` | BLOBy – pravděpodobně XML dávky |
|
||||||
|
| `CHYBA` | příznak chyby (1 znak) |
|
||||||
|
| `STAV` | stav podání (smallint) |
|
||||||
|
| `ID_PODANI` | ID podání u pojišťovny (32 znaků, UUID) |
|
||||||
|
| `IDPODANI` | interní ID podání |
|
||||||
|
| `IDCERT` | certifikát použitý pro podání |
|
||||||
|
| `DAVKA_ROK`, `DAVKA_DISK`, `DAVKA_IDICZ` | metadata dávky |
|
||||||
|
| `BB_DAVKA`, `BB_FAKTURA` | ? |
|
||||||
|
| `DAVKA_DATUMOD`, `DAVKA_DATUMDO` | období dávky |
|
||||||
|
| `DAVKA_CASTKA` | částka dávky |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schéma vazeb
|
||||||
|
|
||||||
|
```
|
||||||
|
FAK (faktura)
|
||||||
|
├── FAKDET (IDFAK → FAK.ID) – finanční breakdown podle lékaře
|
||||||
|
├── FAKDAV (IDFAK → FAK.ID) – seznam dávek ve faktuře
|
||||||
|
├── PORTAL (IDFAK → FAK.ID) – viz níže, IDFAK bývá NULL
|
||||||
|
└── ICZ (IDICZ → ICZ.IDICZ) – ordinace (jen jedna, neřešit)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UZIVATEL – lékaři a uživatelé
|
||||||
|
|
||||||
|
| IDUZI | Příjmení | Jméno | Zkratka | Aktivní |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 2 | Jourová | Jana | JOU | F (neaktivní) |
|
||||||
|
| 3 | Sestra | — | SES | F (neaktivní) |
|
||||||
|
| 4 | Buzalková | Michaela | MBU | T – **lékařka, manželka** |
|
||||||
|
| 5 | Ševčíková | Ivana | ISE | T |
|
||||||
|
| 6 | Buzalka | Vladimír | VBU | T – **uživatel (já)** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ukázkové záznamy
|
||||||
|
|
||||||
|
### FAK ID=856 (poslední)
|
||||||
|
- Faktura č. 260026, pojišťovna **205 (ČPZP)**, prosinec 2025
|
||||||
|
- Vystavena: 2026-03-12, druh: konečná
|
||||||
|
- VYKONY: 2 528,99 Kč, KAPITACE: 0, CENA: **2 528,99 Kč**
|
||||||
|
- ZAPLACENO: NULL, PROPLACENO: NULL
|
||||||
|
|
||||||
|
FAKDET k FAK ID=856:
|
||||||
|
| ID | IDUZI | CENAVYK | CENALEC | CENAKAP |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 1034 | 0 (systém) | 0 | 0 | 1 902,54 |
|
||||||
|
| 1035 | 4 (Buzalková) | 2 528,99 | 0 | 0 |
|
||||||
|
|
||||||
|
### FAK ID=855 (předposlední)
|
||||||
|
- Faktura č. 260026, pojišťovna **207 (OZP)**, prosinec 2025
|
||||||
|
- Vystavena: 2026-03-01, druh: konečná
|
||||||
|
- VYKONY: 85 Kč, KAPITACE: 0, CENA: **85 Kč**
|
||||||
|
- ZAPLACENO: NULL, PROPLACENO: NULL
|
||||||
|
|
||||||
|
FAKDET k FAK ID=855:
|
||||||
|
| ID | IDUZI | CENAVYK | CENALEC | CENAKAP |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 1033 | 6 (Buzalka Vladimír) | 85 | 0 | 0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PORTAL – upřesnění (zjištěno 2026-03-28)
|
||||||
|
|
||||||
|
PORTAL **nesouvisí s fakturací**. Je to samostatná agenda – evidence **registračních dávek** odeslaných pojišťovně.
|
||||||
|
|
||||||
|
### Co PORTAL skutečně je
|
||||||
|
- Medicus přes PORTAL odesílá pojišťovně dávky s přihlášením/odhlášením pacientů k lékaři (MUDr. Buzalková jako praktický lékař)
|
||||||
|
- Bez peněz – čistě administrativní agenda registrací
|
||||||
|
- **IDFAK = NULL** u všech registračních podání (není vazba na fakturu)
|
||||||
|
|
||||||
|
### Klíčové sloupce
|
||||||
|
| Sloupec | Popis |
|
||||||
|
|---|---|
|
||||||
|
| `KDAVKA` | odeslaná dávka – pevný formát (hlavička + řádky I = pacienti, RC, datum) |
|
||||||
|
| `DATA` | odpověď pojišťovny jako XML (`<statuscode>100</statuscode>` = OK) |
|
||||||
|
| `ID_PODANI` | ID přidělené pojišťovnou (např. `D01F260118593316.D01`) |
|
||||||
|
| `DAVKA_DISK` | číslo dávky v roce (sekvenční) |
|
||||||
|
| `DAVKA_ROK` | rok dávky |
|
||||||
|
| `DAVKA_DATUMOD/DO` | období registrací v dávce |
|
||||||
|
| `CHYBA` | F = bez chyby |
|
||||||
|
|
||||||
|
### Ukázka KDAVKA (hlavička dávky)
|
||||||
|
```
|
||||||
|
DP80093050000900202506 15 1 0 0.00180:6.2.46
|
||||||
|
H21109305001 179202506001
|
||||||
|
I 1Příjmení Jméno RC datum_registrace
|
||||||
|
I 2...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ukázka DATA (odpověď pojišťovny)
|
||||||
|
```xml
|
||||||
|
<ekomunikace timestamp="20260127 063105">
|
||||||
|
<session id="ff59b436-...">
|
||||||
|
<statusline>Přijetí registrací proběhlo úspěšně</statusline>
|
||||||
|
<statuscode>100</statuscode>
|
||||||
|
<idpodani>D01F260118593316.D01</idpodani>
|
||||||
|
</session>
|
||||||
|
</ekomunikace>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PORTAL – dva typy dávek
|
||||||
|
|
||||||
|
### Typ 1 – Registrační dávka (DP80)
|
||||||
|
- KDAVKA začíná `DP80`
|
||||||
|
- **IDFAK = NULL** – bez vazby na fakturu
|
||||||
|
- Bez peněz (`DAVKA_CASTKA` = NULL)
|
||||||
|
- Přihlašuje/odhlašuje pacienty k lékaři (MUDr. Buzalková)
|
||||||
|
- Řádky `I` = pacient (RC, datum registrace)
|
||||||
|
|
||||||
|
### Typ 2 – Výkonová dávka (DP98)
|
||||||
|
- KDAVKA začíná `DP98`
|
||||||
|
- **IDFAK = ID faktury** – napojeno na FAK
|
||||||
|
- Má peníze (`DAVKA_CASTKA`, např. 15 452,91 Kč)
|
||||||
|
- `STAV = 10` (registrační mají NULL)
|
||||||
|
- Obsahuje výkony vykázané pojišťovně za dané období
|
||||||
|
|
||||||
|
**Struktura výkonové dávky (KDAVKA):**
|
||||||
|
```
|
||||||
|
DP98... – hlavička dávky (IČP, rok, měsíc, disk, počet případů, částka)
|
||||||
|
A ... – ambulantní případ (RC pacienta, diagnóza)
|
||||||
|
V ... – výkon (datum, kód výkonu, body)
|
||||||
|
Z ... – ZULP (zvlášť účtovaný léčivý přípravek)
|
||||||
|
L ... – lékový řádek (kód, množství, cena)
|
||||||
|
DP05... – druhá část dávky (prevence / jiný typ)
|
||||||
|
P ... – preventivní prohlídka
|
||||||
|
```
|
||||||
|
|
||||||
|
**Odpověď pojišťovny pro výkonovou dávku:**
|
||||||
|
```xml
|
||||||
|
<statusline>Přijetí faktury proběhlo úspěšně</statusline>
|
||||||
|
<statuscode>100</statuscode>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Poznámky
|
||||||
|
- Faktury jsou vystavovány zvlášť pro každou pojišťovnu
|
||||||
|
- FAKDET má řádky zvlášť pro každého lékaře (IDUZI)
|
||||||
|
- IDUZI=0 v FAKDET = systémový záznam pro kapitaci
|
||||||
|
- Kapitace se v FAK.KAPITACE neukazuje (je 0), ale v FAKDET.CENAKAP ano – nutno ověřit
|
||||||
|
- PORTAL = registrační dávky, nesouvisí s fakturací, IDFAK bývá NULL
|
||||||
|
|
||||||
|
## Kódování KDAVKA/FDAVKA – důležité!
|
||||||
|
|
||||||
|
Dávkové soubory (KDAVKA, FDAVKA) jsou uloženy v **CP852** (DOS Latin-2, prahistorické kódování).
|
||||||
|
|
||||||
|
fdb je čte přes connection charset win1250 a vrací je jako Python `str` (špatně dekódované).
|
||||||
|
|
||||||
|
**Správný postup dekódování:**
|
||||||
|
```python
|
||||||
|
# fdb vrátil string dekódovaný jako win1250 – musíme to zvrátit
|
||||||
|
raw_bytes = s.encode('cp1250', errors='replace')
|
||||||
|
spravny_text = raw_bytes.decode('cp852', errors='replace')
|
||||||
|
```
|
||||||
|
|
||||||
|
- **KDAVKA, FDAVKA** → cp852
|
||||||
|
- **DATA** (XML odpověď pojišťovny) → cp1250 (win1250), fdb dekóduje správně
|
||||||
Reference in New Issue
Block a user