Přidán list ED_PODANI + ED_PODANI_DATA do faktury_report.py; doplněny poznámky o eDávkách
- faktury_report.py: nový list ED_PODANI (ED_BOOKOFSUBMISSIONS) s přehledem podání pojišťovnám - faktury_report.py: nový list ED_PODANI_DATA s dekódovaným obsahem dávek (KDAVKA, REQUEST XML, odpovědi pojišťoven) - Opraveno kódování: KDAVKA=cp1250, REQUEST detekce BOM (utf-16/utf-8), SERVERRESPONSE/PROTOCOL=iso-8859-2 - Hyperlinky ED_PODANI ↔ ED_PODANI_DATA a Faktura → FAK - FakturaceADavky.md: dokumentace ED_* tabulek, portálů pojišťoven, formátů REQUEST XML - Průzkumné skripty: find_edavky_table, explore_hpn, explore_ed_bookofsubmissions, parse_trace_edavky aj. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -262,6 +262,224 @@ for i, row in enumerate(fak_rows, start=2):
|
||||
else:
|
||||
cell.value = ''
|
||||
|
||||
# =====================
|
||||
# List 5 – ED_PODANI (ED_BOOKOFSUBMISSIONS)
|
||||
# =====================
|
||||
|
||||
ws5 = wb.create_sheet('ED_PODANI')
|
||||
|
||||
REQUESTTYPE_MAP = {0: 'Registrační', 1: 'Výkonová'}
|
||||
HICCODE_MAP = {
|
||||
'111': 'VZP', '201': 'VoZP', '205': 'ČPZP',
|
||||
'207': 'OZP', '209': 'ZPŠ', '211': 'ZPMV',
|
||||
'212': '212', '213': '213', '217': '217', '228': '228', '333': '333',
|
||||
}
|
||||
|
||||
cur.execute('''
|
||||
SELECT
|
||||
ID, CREATED, SENTDATE, HICCODE, REQUESTTYPE,
|
||||
SUBMISSIONID, INVOICENUMBER,
|
||||
PERIODFROM, PERIODTO, TOTALSUM,
|
||||
STATE, CREATOR, HCPPERSONNAME, HCPCODE, UNIQUEID
|
||||
FROM ED_BOOKOFSUBMISSIONS
|
||||
ORDER BY ID DESC
|
||||
''')
|
||||
ed_cols_raw = [d[0] for d in cur.description]
|
||||
ed_rows = cur.fetchall()
|
||||
|
||||
# Lidštější záhlaví
|
||||
ed_headers = [
|
||||
'ID', 'Vytvořeno', 'Odesláno', 'ZP', 'Typ podání',
|
||||
'Podací č.', 'Faktura',
|
||||
'Období od', 'Období do', 'Částka (Kč)',
|
||||
'Stav', 'Autor', 'Lékař', 'IČZ', 'UUID',
|
||||
]
|
||||
ws5.append(ed_headers)
|
||||
|
||||
# Mapa CISFAK → řádek na listu FAK (pro hyperlink)
|
||||
cisfak_to_fak_row = {}
|
||||
for i, row in enumerate(fak_rows, start=2):
|
||||
cisfak = row[1] # CISFAK je druhý sloupec z DB (index 1)
|
||||
if cisfak and cisfak not in cisfak_to_fak_row:
|
||||
cisfak_to_fak_row[cisfak] = i
|
||||
|
||||
for i, row in enumerate(ed_rows, start=2):
|
||||
(eid, created, sentdate, hiccode, reqtype,
|
||||
submid, invoicenum,
|
||||
perfrom, perto, totalsum,
|
||||
state, creator, hcpperson, hcpcode, uniqueid) = row
|
||||
|
||||
out = [
|
||||
eid,
|
||||
fmt(created),
|
||||
fmt(sentdate),
|
||||
f"{hiccode} {HICCODE_MAP.get(str(hiccode), '')}" if hiccode else '',
|
||||
REQUESTTYPE_MAP.get(reqtype, str(reqtype)) if reqtype is not None else '',
|
||||
fmt(submid),
|
||||
fmt(invoicenum),
|
||||
fmt(perfrom),
|
||||
fmt(perto),
|
||||
float(totalsum) if totalsum is not None else '',
|
||||
fmt(state),
|
||||
fmt(creator),
|
||||
fmt(hcpperson),
|
||||
fmt(hcpcode),
|
||||
fmt(uniqueid),
|
||||
]
|
||||
ws5.append(out)
|
||||
if i % 2 == 0:
|
||||
for cell in ws5[i]:
|
||||
cell.fill = ZEBRA_FILL
|
||||
|
||||
style_header(ws5)
|
||||
ws5.freeze_panes = 'A2'
|
||||
autofit(ws5)
|
||||
|
||||
# Hyperlink: Faktura (sloupec 7) → list FAK
|
||||
for i, row in enumerate(ed_rows, start=2):
|
||||
invoicenum = row[6] # INVOICENUMBER
|
||||
if invoicenum and invoicenum in cisfak_to_fak_row:
|
||||
cell = ws5.cell(row=i, column=7)
|
||||
# +1 kvůli vloženému sloupci FAKDET na listu FAK
|
||||
cell.hyperlink = f'#FAK!B{cisfak_to_fak_row[invoicenum]}'
|
||||
cell.font = LINK_FONT
|
||||
|
||||
# =====================
|
||||
# List 6 – ED_PODANI_DATA (BLOBy z ED_BOOKOFSUBMISSIONS)
|
||||
# =====================
|
||||
|
||||
ws6 = wb.create_sheet('ED_PODANI_DATA')
|
||||
|
||||
cur.execute('''
|
||||
SELECT ID, HICCODE, SENTDATE, SUBMISSIONID, INVOICENUMBER,
|
||||
KDAVKACONTENT, REQUEST, SERVERRESPONSE, PROTOCOL
|
||||
FROM ED_BOOKOFSUBMISSIONS
|
||||
ORDER BY ID DESC
|
||||
''')
|
||||
ed_data_rows = cur.fetchall()
|
||||
|
||||
ed_data_headers = ['ID', 'ZP', 'Odesláno', 'Podací č.', 'Faktura',
|
||||
'KDAVKA', 'REQUEST (XML)', 'SERVERRESPONSE', 'PROTOCOL']
|
||||
ws6.append(ed_data_headers)
|
||||
|
||||
def decode_ed_blob(v, enc):
|
||||
"""Dekóduj BLOB z ED_BOOKOFSUBMISSIONS."""
|
||||
if v is None:
|
||||
return ''
|
||||
if hasattr(v, 'read'):
|
||||
raw = v.read()
|
||||
else:
|
||||
raw = v
|
||||
if not raw:
|
||||
return ''
|
||||
if isinstance(raw, str):
|
||||
# fdb vrátil jako win1250 string – zrekonstruuj bytes
|
||||
raw = raw.encode('cp1250', errors='replace')
|
||||
try:
|
||||
text = raw.decode(enc, errors='replace')
|
||||
except Exception:
|
||||
return repr(raw[:200])
|
||||
# Odstraň prázdné řádky a sjednoť odřádkování
|
||||
lines = [l for l in text.splitlines() if l.strip()]
|
||||
return '\n'.join(lines)
|
||||
|
||||
for i, row in enumerate(ed_data_rows, start=2):
|
||||
eid, hiccode, sentdate, submid, invoicenum, kdavka, request, serverresp, protocol = row
|
||||
|
||||
# KDAVKACONTENT – fdb vrací str přes win1250 spojení, použij přímo
|
||||
if kdavka is None:
|
||||
kdavka_txt = ''
|
||||
else:
|
||||
raw = kdavka.read() if hasattr(kdavka, 'read') else kdavka
|
||||
if isinstance(raw, bytes):
|
||||
raw = raw.encode('latin-1', errors='replace').decode('cp852', errors='replace')
|
||||
lines = [l for l in raw.splitlines() if l.strip()]
|
||||
kdavka_txt = '\n'.join(lines)
|
||||
|
||||
# REQUEST – XML; může být uložen jako UTF-16 binary nebo jako ASCII/UTF-8 string
|
||||
request_txt = ''
|
||||
if request is not None:
|
||||
raw = request.read() if hasattr(request, 'read') else request
|
||||
if isinstance(raw, str):
|
||||
raw_b = raw.encode('latin-1', errors='replace')
|
||||
else:
|
||||
raw_b = raw
|
||||
if raw_b:
|
||||
# Detekce podle BOM – jedině tehdy jde o skutečné UTF-16 binární data
|
||||
if raw_b[:2] in (b'\xff\xfe', b'\xfe\xff'):
|
||||
request_txt = raw_b.decode('utf-16', errors='replace')
|
||||
elif raw_b[:1] == b'<':
|
||||
# ASCII/UTF-8 XML – fdb ho vrátil jako string, použij přímo
|
||||
request_txt = raw_b.decode('utf-8', errors='replace')
|
||||
else:
|
||||
request_txt = raw_b.decode('cp1250', errors='replace')
|
||||
lines = [l for l in request_txt.splitlines() if l.strip()]
|
||||
request_txt = '\n'.join(lines)
|
||||
|
||||
# SERVERRESPONSE a PROTOCOL – latin-1 re-encoding, pak iso-8859-2
|
||||
def decode_latin_blob(v, enc):
|
||||
if v is None:
|
||||
return ''
|
||||
raw = v.read() if hasattr(v, 'read') else v
|
||||
if not raw:
|
||||
return ''
|
||||
if isinstance(raw, str):
|
||||
raw = raw.encode('latin-1', errors='replace')
|
||||
text = raw.decode(enc, errors='replace')
|
||||
lines = [l for l in text.splitlines() if l.strip()]
|
||||
return '\n'.join(lines)
|
||||
|
||||
serverresp_txt = decode_latin_blob(serverresp, 'iso-8859-2')
|
||||
protocol_txt = decode_latin_blob(protocol, 'iso-8859-2')
|
||||
|
||||
out = [
|
||||
eid,
|
||||
f"{hiccode} {HICCODE_MAP.get(str(hiccode), '')}" if hiccode else '',
|
||||
fmt(sentdate),
|
||||
fmt(submid),
|
||||
fmt(invoicenum),
|
||||
kdavka_txt,
|
||||
request_txt,
|
||||
serverresp_txt,
|
||||
protocol_txt,
|
||||
]
|
||||
ws6.append(out)
|
||||
if i % 2 == 0:
|
||||
for cell in ws6[i]:
|
||||
cell.fill = ZEBRA_FILL
|
||||
for cell in ws6[i]:
|
||||
cell.alignment = WRAP
|
||||
ws6.row_dimensions[i].height = 80
|
||||
|
||||
style_header(ws6)
|
||||
ws6.freeze_panes = 'A2'
|
||||
|
||||
# Šířky sloupců ED_PODANI_DATA
|
||||
for col, width in zip(['A','B','C','D','E','F','G','H','I'],
|
||||
[6, 10, 12, 16, 14, 80, 60, 60, 40]):
|
||||
ws6.column_dimensions[col].width = width
|
||||
|
||||
# Hyperlinky ED_PODANI ↔ ED_PODANI_DATA (přes ID)
|
||||
ed_id_to_ws5_row = {row[0]: i for i, row in enumerate(ed_rows, start=2)}
|
||||
ed_id_to_ws6_row = {row[0]: i for i, row in enumerate(ed_data_rows, start=2)}
|
||||
|
||||
# ED_PODANI sloupec 1 (ID) → ED_PODANI_DATA
|
||||
for i, row in enumerate(ed_rows, start=2):
|
||||
eid = row[0]
|
||||
if eid in ed_id_to_ws6_row:
|
||||
cell = ws5.cell(row=i, column=1)
|
||||
cell.hyperlink = f'#ED_PODANI_DATA!A{ed_id_to_ws6_row[eid]}'
|
||||
cell.font = LINK_FONT
|
||||
|
||||
# ED_PODANI_DATA sloupec 1 (ID) → ED_PODANI
|
||||
for i, row in enumerate(ed_data_rows, start=2):
|
||||
eid = row[0]
|
||||
if eid in ed_id_to_ws5_row:
|
||||
cell = ws6.cell(row=i, column=1)
|
||||
cell.hyperlink = f'#ED_PODANI!A{ed_id_to_ws5_row[eid]}'
|
||||
cell.font = LINK_FONT
|
||||
cell.alignment = WRAP
|
||||
|
||||
# =====================
|
||||
# Uložení
|
||||
# =====================
|
||||
@@ -269,4 +487,4 @@ for i, row in enumerate(fak_rows, start=2):
|
||||
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'))
|
||||
sys.stdout.buffer.write(f'FAK: {len(fak_rows)} radku, FAKDET: {len(det_rows)} radku, PORTAL: {len(portal_rows)} radku, ED_PODANI: {len(ed_rows)} radku\n'.encode('utf-8'))
|
||||
|
||||
Reference in New Issue
Block a user