""" Consulta a API de parcelas por installmentGroupCode (orderNumber) e grava em DocPedidos + DocPedidosParcelas. Fonte dos códigos: SELECT orderNumber FROM Grgb_fiscal_invoices_items """ import os import time import requests import installments_reader from installments_reader import Auth, get_installments_page, upsert_doc_pedidos_sqlserver # ─── CONFIGURE AQUI ─────────────────────────────────────────────────────────── WRITE_SQL = True # True = grava no banco | False = só lê e exibe SKIP_EXISTING = True # True = pula códigos já gravados em DocPedidos BATCH_SIZE = 10 # grava no banco a cada N pedidos processados MIN_INTERVAL_MS = 3500 # ms mínimo entre chamadas à API # ────────────────────────────────────────────────────────────────────────────── SQL_CONN = ( "DRIVER={ODBC Driver 17 for SQL Server};" "SERVER=10.77.77.10;" "DATABASE=GINSENG;" "UID=andrey;" "PWD=88253332;" "TrustServerCertificate=yes;" ) def _get_order_numbers() -> list[str]: import pyodbc cn = pyodbc.connect(SQL_CONN, timeout=30) cur = cn.cursor() cur.execute( "SELECT DISTINCT [orderNumber] " "FROM [GINSENG].[dbo].[Grgb_fiscal_invoices_items] " "WHERE [orderNumber] IS NOT NULL AND LTRIM(RTRIM(CAST([orderNumber] AS VARCHAR))) <> ''" ) return [str(row[0]).strip() for row in cur.fetchall()] def _get_existing_group_codes() -> set[str]: import pyodbc try: cn = pyodbc.connect(SQL_CONN, timeout=30) cur = cn.cursor() cur.execute("SELECT InstallmentGroupCode FROM dbo.DocPedidos") return {str(row[0]).strip() for row in cur.fetchall()} except Exception: return set() def _fetch_all_pages(session: requests.Session, auth: Auth, group_code: str) -> list: all_items = [] page = 1 while True: body = get_installments_page( session=session, auth=auth, start_date=None, end_date=None, installment_change=None, mediator_code=None, page=page, installment_group_code=group_code, cookie_header=None, ) items = ((body.get("data") or {}).get("installments")) or [] all_items.extend(items) pagination = (body.get("data") or {}).get("pagination") or {} total = int(pagination.get("total") or 0) limit = int(pagination.get("limit") or len(items) or 10) total_pages = max(1, (total + limit - 1) // limit) if total else page print(f" pagina={page}/{total_pages} itens={len(items)}") if not items or page >= total_pages: break page += 1 return all_items def _flush(batch: list, label: str = "") -> dict: if not batch or not WRITE_SQL: return {} stats = upsert_doc_pedidos_sqlserver(batch, SQL_CONN) print( f"[sql] {label} " f"pedidos={stats.get('pedidos', 0)} parcelas={stats.get('parcelas', 0)}" ) return stats def main() -> None: os.environ.setdefault("INSTALLMENTS_MIN_INTERVAL_MS", str(MIN_INTERVAL_MS)) os.environ.setdefault("INSTALLMENTS_429_MIN_WAIT_SEC", "45") os.environ.setdefault("THROTTLE_RECOVERY_PAUSE_SEC", "900") print("[info] buscando orderNumbers no banco...") order_numbers = _get_order_numbers() print(f"[info] total encontrado: {len(order_numbers)}") existing: set[str] = set() if SKIP_EXISTING: existing = _get_existing_group_codes() print(f"[info] já em DocPedidos: {len(existing)} — serão pulados") pending = [c for c in order_numbers if c not in existing] print(f"[info] para consultar na API: {len(pending)}\n") session = requests.Session() session.trust_env = False auth = Auth(session) batch: list = [] batch_first_idx: int = 0 batch_first_code: str = "" total_pedidos = 0 total_parcelas = 0 erros = 0 start_ts = time.monotonic() def _fmt(s: float) -> str: s = int(s) if s < 60: return f"{s}s" if s < 3600: return f"{s // 60}m:{s % 60:02d}s" return f"{s // 3600}h:{(s % 3600) // 60:02d}m" for idx, group_code in enumerate(pending, 1): elapsed = time.monotonic() - start_ts avg_s = elapsed / idx restante = avg_s * (len(pending) - idx) print(f"[{idx}/{len(pending)}] group_code={group_code} " f"decorrido={_fmt(elapsed)} restante~{_fmt(restante)}") try: items = _fetch_all_pages(session, auth, group_code) if not items: print(" sem dados na API") continue if not batch: batch_first_idx = idx batch_first_code = group_code batch.append({ "installmentGroupCode": group_code, "rawResponse": {"data": {"installments": items}}, "installments": items, }) if len(batch) >= BATCH_SIZE: label = (f"inserindo consultas {batch_first_idx}..{idx} " f"(group {batch_first_code} até {group_code})") stats = _flush(batch, label) total_pedidos += stats.get("pedidos", 0) total_parcelas += stats.get("parcelas", 0) batch = [] batch_first_idx = 0 batch_first_code = "" except Exception as exc: print(f" [erro] {exc}") erros += 1 # flush final if batch: label = (f"inserindo consultas {batch_first_idx}..{len(pending)} " f"(group {batch_first_code} até {batch[-1]['installmentGroupCode']})") else: label = "flush final" stats = _flush(batch, label) total_pedidos += stats.get("pedidos", 0) total_parcelas += stats.get("parcelas", 0) print( f"\n[fim] pedidos={total_pedidos} parcelas={total_parcelas} erros={erros}" ) if __name__ == "__main__": main()