diff --git a/routers/crm_extras_router.py b/routers/crm_extras_router.py index bfbc965..9c851dd 100644 --- a/routers/crm_extras_router.py +++ b/routers/crm_extras_router.py @@ -275,7 +275,8 @@ def export_clanovi_xlsx( ws.cell(row=ridx, column=cidx, value=v) # Auto column widths - for col_letter, h in zip("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "AA AB AC AD".split(), headers): + col_letters = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + ["AA", "AB", "AC", "AD", "AE", "AF"] + for col_letter, h in zip(col_letters, headers): ws.column_dimensions[col_letter].width = max(10, min(28, len(h) + 4)) ws.freeze_panes = "A2" @@ -406,22 +407,35 @@ def crm_stats(klub_id: Optional[int] = Query(None)): class NotifScanIn(BaseModel): klub_id: Optional[int] = None thresholds: Optional[list[int]] = None # default = LIJEC_THRESHOLDS + include_expired: bool = True # uključi i one koji su već istekli @router.post("/lijecnicki/notify-scan") def lijecnicki_notify_scan(body: NotifScanIn): """ Skenira nadolazeće isteke i kreira notifikacije (InApp + Email mock) - za pragove 30/15/7 dana. Ne duplicira: gleda meta.lijecnicki_id+threshold - u zadnjih 7 dana. + za pragove 30/15/7 dana. Ako include_expired=True, isto kreira jednu + notifikaciju (threshold=0) za već istekle. + Ne duplicira: gleda meta.lijecnicki_id+threshold u zadnjih 7 dana. """ thresholds = sorted(set(body.thresholds or LIJEC_THRESHOLDS), reverse=True) klub_filter = "AND l.klub_id = %s" if body.klub_id else "" klub_params = [body.klub_id] if body.klub_id else [] + # threshold=0 → već istekli (poseban "expired" bucket) + scan_buckets = [(thr, "uskoro") for thr in thresholds] + if body.include_expired: + scan_buckets.append((0, "expired")) + created = [] with _conn() as conn, conn.cursor() as cur: - for thr in thresholds: + for thr, kind in scan_buckets: + if kind == "expired": + where_window = "(l.vrijedi_do - CURRENT_DATE) < 0" + where_params = [] + else: + where_window = "(l.vrijedi_do - CURRENT_DATE) BETWEEN 0 AND %s" + where_params = [thr] cur.execute(f""" SELECT l.id, l.vrijedi_do, l.clan_id, (l.vrijedi_do - CURRENT_DATE)::int AS dana, @@ -432,27 +446,9 @@ def lijecnicki_notify_scan(body: NotifScanIn): LEFT JOIN pgz_sport.clanovi cl ON cl.id = l.clan_id LEFT JOIN pgz_sport.klubovi k ON k.id = l.klub_id WHERE l.vrijedi_do IS NOT NULL - AND (l.vrijedi_do - CURRENT_DATE) BETWEEN 0 AND %s - AND (l.vrijedi_do - CURRENT_DATE) > %s + AND {where_window} {klub_filter} - """, [thr, thr - 1] + klub_params if False else - ([thr - (thresholds[thresholds.index(thr)+1] if thresholds.index(thr)+1 < len(thresholds) else 0), - -1] + klub_params)) - # Pojednostavljen scan: samo "≤ thr & > prev_thr" dovodi do duplika; - # umjesto toga samo gledamo "u prozoru ≤ thr". - cur.execute(f""" - SELECT l.id, l.vrijedi_do, l.clan_id, - (l.vrijedi_do - CURRENT_DATE)::int AS dana, - cl.ime || ' ' || cl.prezime AS clan, - cl.email AS clan_email, - k.naziv AS klub - FROM pgz_sport.lijecnicki_pregledi l - LEFT JOIN pgz_sport.clanovi cl ON cl.id = l.clan_id - LEFT JOIN pgz_sport.klubovi k ON k.id = l.klub_id - WHERE l.vrijedi_do IS NOT NULL - AND (l.vrijedi_do - CURRENT_DATE) BETWEEN 0 AND %s - {klub_filter} - """, [thr] + klub_params) + """, where_params + klub_params) kandidati = [_row(r) for r in cur.fetchall()] for r in kandidati: @@ -467,11 +463,16 @@ def lijecnicki_notify_scan(body: NotifScanIn): if cur.fetchone(): continue - subject = f"⚕ Liječnički pregled ističe za {r['dana']} dana: {r['clan']}" + if r['dana'] is not None and r['dana'] < 0: + subject = f"⚠ Liječnički pregled ISTEKAO ({-r['dana']} dana): {r['clan']}" + msg_dana = f"istekao prije {-r['dana']} dana" + else: + subject = f"⚕ Liječnički pregled ističe za {r['dana']} dana: {r['clan']}" + msg_dana = f"{r['dana']} dana ostalo" body_txt = ( f"Liječnički pregled za sportaša {r['clan']} " - f"({r.get('klub') or '(bez kluba)'}) ističe {r['vrijedi_do']} " - f"— {r['dana']} dana ostalo.\n\n" + f"({r.get('klub') or '(bez kluba)'}) — vrijedi do {r['vrijedi_do']} " + f"— {msg_dana}.\n\n" f"Molimo zakažite novi termin u ZZJZ PGŽ " f"(ili koristite /sport/api/crm/lijecnicki/{r['id']}/zakazi).\n\n" f"PGŽ Sport ERP/CRM"