This commit is contained in:
2026-06-23 11:53:32 +02:00
parent 1cb825dcff
commit d1422d9038
11 changed files with 242 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
"""_process_batch_v0.py — DOČASNÉ: dávka 2 faktur (T-Mobile osobní, Perlička ordinace)."""
import sys
from datetime import date
from pathlib import Path
sys.stdout.reconfigure(encoding="utf-8")
sys.path.insert(0, r"U:\ordinaceprojekt")
import dropbox, msal, requests
from Knihovny.telegram_notify import zeptej_se_telegram, posli_telegram
today = date.today(); ddmmyy = today.strftime("%d%m%y"); fdate = today.strftime("%Y-%m-%d")
payments = [
dict(name="T-Mobile", payer="2100046291", acct="19-2235210247", hal=60920,
vs="9953395180", bank="0100", ks="0000", ss="", av="faktura T-Mobile 20MAY-19JUN2026",
summary="T-Mobile 06-2026", mailbox="vladimir.buzalka@buzalka.cz",
msg_id="AAMkADY2MzQ0N2JkLWE4NjAtNDNjYS05OTVlLTQxYjhkYWVlZmY1NQBGAAAAAACkV0glv2dZRLb1sPnwFvdvBwAzGo7b-cvrRoXjMGfWnzDsAAAAAAEMAAAzGo7b-cvrRoXjMGfWnzDsAALtAR6DAAA="),
dict(name="Perlicka", payer="2800046620", acct="2603293964", hal=322000,
vs="2026474", bank="2010", ks="0000", ss="", av="Perlicka fa 2026474",
summary="Perlička fa 2026474", mailbox="ordinace@buzalka.cz",
msg_id="AQMkADYxOTE5ZTYwLTg2MWItNDNmOC04MWE5LTczZjM0NmJmMTRlYwBGAAADlMSYEfdwGUe6ACarqZh2ygcAszvBQpZaWUKlscfbC8_jxwAAAgEMAAAAszvBQpZaWUKlscfbC8_jxwABaPXXzgAAAA=="),
]
def build(p):
ssfield = p["ss"] if p["ss"] else " "
item = f"{p['acct']} {str(p['hal']).zfill(12)} {p['vs']} {p['bank']}{p['ks']} {ssfield} AV:{p['av']}"
lines = ["UHL1"+ddmmyy+" "*20+"0"*28, "1 1501 000000 2010",
f"2 000000-{p['payer']} {str(p['hal']).zfill(14)} {ddmmyy}", item, "3 +", "5 +"]
return ("\r\n".join(lines)+"\r\n").encode("ascii")
for p in payments:
p["data"] = build(p)
p["fname"] = f"{fdate} KPC k platbě [{p['summary']}].kpc"
print(f"=== {p['fname']} ({len(p['data'])} B) ===")
print(p["data"].decode("ascii").replace("\r\n","\\r\\n\n"), end="")
print()
msg = (
"💳 Návrh plateb (KPC agent) — 2 faktury\n\n"
"1) T-Mobile 609,20 Kč → 19-2235210247/0100, VS 9953395180 (z osobního)\n"
"2) Perlička 3 220,00 Kč → 2603293964/2010, VS 2026474 (z ordinace)\n\n"
"FIO: obojí nezaplaceno ✅ (Perlička = nový dodavatel)\n\n"
"Vytvořit oba KPC a nahrát do Dropboxu? Odpověz: ano / ne"
)
print(">> Potvrzeno v chatu (ano) — pokračuji bez Telegram dotazu.")
# Dropbox + Graph
env = {}
for line in Path(r"U:\PythonProject\Janssen\EmailsImport\.env").read_text(encoding="utf-8").splitlines():
line=line.strip()
if "=" in line and not line.startswith("#"):
k,v=line.split("=",1); env[k.strip()]=v.strip()
dbx = dropbox.Dropbox(app_key=env["DROPBOX_APP_KEY"], app_secret=env["DROPBOX_APP_SECRET"],
oauth2_refresh_token=env["DROPBOX_APP_REFRESH_TOKEN"])
TENANT="7d269944-37a4-43a1-8140-c7517dc426e9"; CID="4b222bfd-78c9-4239-a53f-43006b3ed07f"
SECRET="Txg8Q~MjhocuopxsJyJBhPmDfMxZ2r5WpTFj1dfk"
app=msal.ConfidentialClientApplication(CID,authority=f"https://login.microsoftonline.com/{TENANT}",client_credential=SECRET)
tok=app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
H={"Authorization":f"Bearer {tok['access_token']}","Content-Type":"application/json"}
out_dir = Path(__file__).parent/"output"; out_dir.mkdir(exist_ok=True)
for p in payments:
(out_dir/p["fname"]).write_bytes(p["data"])
res = dbx.files_upload(p["data"], f"/!!!Days/Downloads Z230/{p['fname']}",
mode=dropbox.files.WriteMode.add, autorename=True)
print(f"NAHRÁNO → {res.path_display}")
rp = requests.patch(f"https://graph.microsoft.com/v1.0/users/{p['mailbox']}/messages/{p['msg_id']}",
headers=H, json={"categories":["KPCzpracovánoClaudem"]}, timeout=30)
print(f"PATCH {p['name']} ({p['mailbox']}): {rp.status_code}", "" if rp.ok else rp.text[:200])
posli_telegram("✅ KPC vytvořeno a nahráno (2):\n• T-Mobile 609,20 Kč (osobní)\n• Perlička 3 220 Kč (ordinace)\nV bankingu stačí podepsat.")
print(">> HOTOVO.")
+114
View File
@@ -0,0 +1,114 @@
"""_process_ordinace_faktury_v0.py — DOČASNÉ: KPC per dodavatel z 8 ordinačních faktur.
Z PDF vytáhne účet+VS (Claude), cross-check účtu proti FIO historii + částky proti názvu,
sestaví per-dodavatel KPC (kpc_format) z ordinace a nahraje do Dropboxu.
"""
import importlib, json, os, re, subprocess, sys
from datetime import date
from pathlib import Path
sys.stdout.reconfigure(encoding="utf-8")
for mod, pkg in (("fitz", "PyMuPDF"), ("requests", "requests"), ("dropbox", "dropbox")):
try: importlib.import_module(mod)
except ImportError: subprocess.check_call([sys.executable, "-m", "pip", "install", "--quiet", pkg])
import fitz, requests, dropbox # noqa: E402
sys.path.insert(0, r"U:\ordinaceprojekt\EmailAgent")
import kpc_format # noqa: E402
def _load_env(p):
p = Path(p)
if p.exists():
for line in p.read_text(encoding="utf-8").splitlines():
line = line.strip()
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1); os.environ.setdefault(k.strip(), v.strip().strip('"').strip("'"))
_load_env(r"U:\ordinaceprojekt\Medevio\.env")
_load_env(r"U:\PythonProject\Janssen\EmailsImport\.env")
PAYER = "2800046620" # ordinace
DIR = r"U:\Dropbox\Ordinace\!!MUDr. Michaela Buzalková s.r.o\Prosek\#040 Faktury přijaté"
# (dodavatel, číslo faktury, částka z názvu, soubor)
ENTRIES = [
("MEDEVIO", "321799", 2497.58, "2026-06-22 Faktura MEDEVIO 321799 [Medevio tarif Ústředna Pokročilá Plus] [2497.58 CZK].pdf"),
("Poliklinika Prosek", "91260794", 464.64, "2026-06-15 Faktura Poliklinika Prosek 91260794 [sterilizace, telefonní poplatek] [464.64 CZK].pdf"),
("Ptáček", "202605002", 3632.10, "2026-06-12 Faktura Ptáček 202605002 [vakcína FSME-IMMUN 0.5 ml] [3632.10 CZK].pdf"),
("Poliklinika Prosek", "96260214", 28363.00, "2026-06-01 Faktura Poliklinika Prosek 96260214 [nájemné a služby] [28363.00 CZK].pdf"),
("Ptáček", "202604906", 1214.08, "2026-06-10 Faktura Ptáček 202604906 [vakcína ADACEL] [1214.08 CZK].pdf"),
("QuickSeal", "110606255", 2620.00, "2026-06-01 Faktura QuickSeal 110606255 [VivaDiag Hydroxyvitamin D3 testy, poštovné] [2620.00 CZK].pdf"),
("ASKER", "261103225", 339.00, "2026-06-01 Faktura ASKER 261103225 [kontejner Yannick 1.5 l] [339.00 CZK].pdf"),
("Ptáček", "202604570", 9235.20, "2026-05-29 Faktura Ptáček 202604570 [vakcíny Adacel, Vaqta, Havrix] [9235.20 CZK].pdf"),
]
# Účty dodavatelů z FIO historie (cross-check proti záměně/chybě extrakce)
FIO_ACCOUNTS = {
"MEDEVIO": ("2701907026", "2010"),
"Poliklinika Prosek": ("1387720540", "2700"),
"Ptáček": ("220205630", "0300"),
"QuickSeal": ("197638875", "0300"),
"ASKER": ("2913796399", "0800"),
}
PROMPT = """Z TEXTU faktury vytáhni BANKOVNÍ ÚDAJE K PLATBĚ. Vrať POUZE JSON:
{"predcisli":"" nebo číslice, "cislo_uctu":"číslo účtu příjemce bez kódu banky",
"smer_kod":"4místný kód banky", "vs":"variabilní symbol k platbě", "castka_kc":číslo,
"ss":"specifický symbol nebo prázdné"}
TEXT FAKTURY:
%(t)s
"""
def claude(text):
r = requests.post("https://api.anthropic.com/v1/messages",
headers={"x-api-key": os.environ["ANTHROPIC_API_KEY"], "anthropic-version": "2023-06-01",
"content-type": "application/json"},
json={"model": "claude-opus-4-8", "max_tokens": 400,
"messages": [{"role": "user", "content": PROMPT % {"t": text[:14000]}}]}, timeout=90)
r.raise_for_status()
t = r.json()["content"][0]["text"]
return json.loads(re.search(r"\{.*\}", t, re.S).group(0))
ddmmrr = date.today().strftime("%d%m%y")
groups = {} # dodavatel -> [Polozka]
report = []
for dod, fakt, castka_nazev, fname in ENTRIES:
txt = "".join(p.get_text() for p in fitz.open(str(Path(DIR) / fname)))
d = claude(txt)
ucet = re.sub(r"\D", "", str(d.get("cislo_uctu", "")))
smer = str(d.get("smer_kod", "")).strip()
vs = str(d.get("vs", "")).strip()
castka = float(d.get("castka_kc") or 0)
flags = []
fio_u, fio_b = FIO_ACCOUNTS.get(dod, ("", ""))
if fio_u and ucet != fio_u:
flags.append(f"ÚČET ≠ FIO ({ucet} vs {fio_u})")
if fio_b and smer != fio_b:
flags.append(f"BANKA ≠ FIO ({smer} vs {fio_b})")
if abs(castka - castka_nazev) > 0.01:
flags.append(f"ČÁSTKA ≠ název ({castka} vs {castka_nazev})")
status = "OK" if not flags else "FLAG: " + "; ".join(flags)
report.append((dod, fakt, castka_nazev, ucet, smer, vs, status))
if not flags:
av = kpc_format._ascii(f"{dod} fa {fakt}")
groups.setdefault(dod, []).append(kpc_format.Polozka(
ucet=kpc_format.fmt_account(ucet, d.get("predcisli", "")),
halere=round(castka_nazev * 100), vs=vs, smer_kod=smer,
ks="0000", ss=str(d.get("ss") or ""), av=av))
print("=== EXTRAKCE + CROSS-CHECK ===")
for dod, fakt, c, ucet, smer, vs, status in report:
print(f"{dod:20} fa {fakt:11} {c:>10.2f}{ucet}/{smer} VS {vs:12} [{status}]")
# Dropbox
dbx = dropbox.Dropbox(app_key=os.environ["DROPBOX_APP_KEY"], app_secret=os.environ["DROPBOX_APP_SECRET"],
oauth2_refresh_token=os.environ["DROPBOX_APP_REFRESH_TOKEN"])
out_dir = Path(__file__).parent / "output"; out_dir.mkdir(exist_ok=True)
fdate = date.today().strftime("%Y-%m-%d")
print("\n=== KPC PER DODAVATEL ===")
for dod, items in groups.items():
data = kpc_format.build_kpc(PAYER, items, ddmmrr=ddmmrr)
summary = f"{dod}" + (f" {len(items)} faktury" if len(items) > 1 else f" fa {[e[1] for e in report if e[0]==dod][0]}")
fname = f"{fdate} KPC k platbě [{summary}].kpc"
(out_dir / fname).write_bytes(data)
print(f"\n--- {fname} ---")
print(data.decode("ascii").replace("\r\n", "\\r\\n\n"), end="")
res = dbx.files_upload(data, f"/!!!Days/Downloads Z230/{fname}",
mode=dropbox.files.WriteMode.add, autorename=True)
print(f"NAHRÁNO → {res.path_display}")
@@ -0,0 +1,6 @@
UHL1220626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000000322000 220626
2603293964 000000322000 2026474 20100000 AV:Perlicka fa 2026474
3 +
5 +
@@ -0,0 +1,6 @@
UHL1220626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2100046291 00000000060920 220626
19-2235210247 000000060920 9953395180 01000000 AV:faktura T-Mobile 20MAY-19JUN2026
3 +
5 +
@@ -0,0 +1,6 @@
UHL1230626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000000033900 230626
2913796399 000000033900 261103225 08000000 AV:ASKER fa 261103225
3 +
5 +
@@ -0,0 +1,6 @@
UHL1230626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000000249758 230626
2701907026 000000249758 321799 20100000 AV:MEDEVIO fa 321799
3 +
5 +
@@ -0,0 +1,7 @@
UHL1230626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000002882764 230626
1387720540 000000046464 91260794 27000000 AV:Poliklinika Prosek fa 91260794
1387720540 000002836300 10626 27000000 AV:Poliklinika Prosek fa 96260214
3 +
5 +
@@ -0,0 +1,6 @@
UHL1230626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000001616540 230626
1387720540 000001616540 91260763 27000000 AV:Poliklinika Prosek fa 91260763
3 +
5 +
@@ -0,0 +1,6 @@
UHL1230626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000000019314 230626
1387720540 000000019314 91260795 27000000 AV:Poliklinika Prosek fa 91260795
3 +
5 +
@@ -0,0 +1,8 @@
UHL1230626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000001408138 230626
220205630 000000363210 202605002 03000000 AV:Ptacek fa 202605002
220205630 000000121408 202604906 03000000 AV:Ptacek fa 202604906
220205630 000000923520 202604570 03000000 AV:Ptacek fa 202604570
3 +
5 +
@@ -0,0 +1,6 @@
UHL1230626 0000000000000000000000000000
1 1501 000000 2010
2 000000-2800046620 00000000262000 230626
197638875 000000262000 110606255 03000000 AV:QuickSeal fa 110606255
3 +
5 +