notebookvb
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
import firebirdsql as 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(
|
||||
host='192.168.1.10',
|
||||
port=3050,
|
||||
database=r'm:\Medicus\data\MEDICUS.FDB',
|
||||
user='SYSDBA', password='masterkey', charset='WIN1250'
|
||||
)
|
||||
cur = conn.cursor()
|
||||
|
||||
# --- Výstupní soubor ---
|
||||
output_dir = r'z:\Dropbox\Ordinace\Reporty'
|
||||
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'))
|
||||
Reference in New Issue
Block a user