att
This commit is contained in:
parent
94a7bc5e1b
commit
230654836b
152
enrich_paytype_backfill.py
Normal file
152
enrich_paytype_backfill.py
Normal file
@ -0,0 +1,152 @@
|
||||
"""
|
||||
Backfill de paymentType para todos os Numero_Pedido em Grgb_vendas_report
|
||||
que ainda não constam em Grgb_installment_paytype.
|
||||
|
||||
Pode ser interrompido e re-executado a qualquer momento — retoma de onde parou.
|
||||
"""
|
||||
import re
|
||||
import time
|
||||
import requests
|
||||
from typing import Optional
|
||||
|
||||
from installments_reader import Auth, get_installments_page
|
||||
|
||||
# ─── CONFIG ───────────────────────────────────────────────────────────────────
|
||||
SQL_CONN = (
|
||||
"DRIVER={ODBC Driver 17 for SQL Server};"
|
||||
"SERVER=10.77.77.10;"
|
||||
"DATABASE=GINSENG;"
|
||||
"UID=andrey;"
|
||||
"PWD=88253332;"
|
||||
"TrustServerCertificate=yes;"
|
||||
)
|
||||
|
||||
INSTALLMENT_GROUP_COL = "Numero_Pedido"
|
||||
RATE_LIMIT_S = 1.5
|
||||
PRINT_EVERY = 1
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _conn_str() -> str:
|
||||
s = SQL_CONN.rstrip(";")
|
||||
if not re.search(r"(?i)\bEncrypt\b", s):
|
||||
s += ";Encrypt=no"
|
||||
return s + ";"
|
||||
|
||||
|
||||
def _ensure_paytype_table(cur) -> None:
|
||||
cur.execute("""
|
||||
IF OBJECT_ID('dbo.Grgb_installment_paytype', 'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.Grgb_installment_paytype (
|
||||
InstallmentGroupCode VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||
PaymentType VARCHAR(80) NULL,
|
||||
ConsultadoEm DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
|
||||
)
|
||||
END
|
||||
""")
|
||||
|
||||
|
||||
def _load_pending(cur) -> list[str]:
|
||||
cur.execute(f"""
|
||||
SELECT DISTINCT [{INSTALLMENT_GROUP_COL}]
|
||||
FROM dbo.Grgb_vendas_report
|
||||
WHERE [{INSTALLMENT_GROUP_COL}] IS NOT NULL
|
||||
AND [{INSTALLMENT_GROUP_COL}] <> ''
|
||||
AND [{INSTALLMENT_GROUP_COL}] NOT IN (
|
||||
SELECT InstallmentGroupCode FROM dbo.Grgb_installment_paytype
|
||||
)
|
||||
ORDER BY [{INSTALLMENT_GROUP_COL}]
|
||||
""")
|
||||
return [str(r[0]) for r in cur.fetchall()]
|
||||
|
||||
|
||||
def _query_payment_type(
|
||||
session: requests.Session,
|
||||
auth: Auth,
|
||||
group_code: str,
|
||||
) -> Optional[str]:
|
||||
"""Usa get_installments_page do installments_reader (headers + retry corretos)."""
|
||||
try:
|
||||
body = get_installments_page(
|
||||
session=session,
|
||||
auth=auth,
|
||||
start_date=None,
|
||||
end_date=None,
|
||||
installment_change=None,
|
||||
mediator_code=None,
|
||||
page=1,
|
||||
installment_group_code=group_code,
|
||||
)
|
||||
installments = body.get("data", {}).get("installments") or []
|
||||
if installments:
|
||||
return str(installments[0].get("paymentType") or "")
|
||||
except Exception as exc:
|
||||
print(f"[erro] groupCode={group_code}: {exc}")
|
||||
return None
|
||||
|
||||
|
||||
def _save(cur, group_code: str, payment_type: Optional[str]) -> None:
|
||||
cur.execute(
|
||||
"INSERT INTO dbo.Grgb_installment_paytype (InstallmentGroupCode, PaymentType) "
|
||||
"VALUES (?, ?)",
|
||||
group_code, payment_type,
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
import pyodbc
|
||||
|
||||
conn_str = _conn_str()
|
||||
|
||||
cn = pyodbc.connect(conn_str, timeout=60)
|
||||
cn.autocommit = False
|
||||
cur = cn.cursor()
|
||||
_ensure_paytype_table(cur)
|
||||
cn.commit()
|
||||
pending = _load_pending(cur)
|
||||
cur.close(); cn.close()
|
||||
|
||||
total = len(pending)
|
||||
print(f"[backfill] {total} pedidos pendentes de paymentType")
|
||||
if not total:
|
||||
print("[backfill] nada a fazer.")
|
||||
return
|
||||
|
||||
session = requests.Session()
|
||||
session.trust_env = False
|
||||
auth = Auth(session)
|
||||
|
||||
ok = erros = 0
|
||||
for i, group_code in enumerate(pending, 1):
|
||||
payment_type = _query_payment_type(session, auth, group_code)
|
||||
|
||||
cn = pyodbc.connect(conn_str, timeout=60)
|
||||
cn.autocommit = False
|
||||
cur = cn.cursor()
|
||||
try:
|
||||
_save(cur, group_code, payment_type)
|
||||
cn.commit()
|
||||
ok += 1
|
||||
except Exception as exc:
|
||||
cn.rollback()
|
||||
print(f"[erro] sql groupCode={group_code}: {exc}")
|
||||
erros += 1
|
||||
finally:
|
||||
cur.close(); cn.close()
|
||||
|
||||
if i % PRINT_EVERY == 0 or i == total:
|
||||
pct = i / total * 100
|
||||
print(
|
||||
f"[backfill] {i}/{total} ({pct:.1f}%)"
|
||||
f" ok={ok} erros={erros}"
|
||||
f" ultimo={group_code} paymentType={payment_type}"
|
||||
)
|
||||
|
||||
time.sleep(RATE_LIMIT_S)
|
||||
|
||||
print(f"\n[backfill] concluido: {ok} gravados, {erros} erros")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -57,6 +57,12 @@ WRITE_SQL = True
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
WATERMARK_FILE = os.path.join(_SCRIPT_DIR, "vendas_watermark.json")
|
||||
IMPORT_BATCH_SIZE = 500 # linhas por lote no SQL Server
|
||||
|
||||
ENRICH_PAYMENT_TYPES = True # True = consulta API de parcelas para preencher paymentType
|
||||
# Nome da coluna em Grgb_vendas_report que contém o installmentGroupCode.
|
||||
# Verifique rodando: SELECT TOP 1 * FROM dbo.Grgb_vendas_report
|
||||
INSTALLMENT_GROUP_COL = "Numero_Pedido"
|
||||
ENRICH_RATE_LIMIT_S = 0.3 # pausa entre chamadas à API de parcelas
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
SQL_CONN = (
|
||||
@ -72,6 +78,10 @@ REPORTS_URL = (
|
||||
"https://bff-portal-apigw.produto-financeiro.grupoboticario.digital"
|
||||
"/v2/franchisee/reports"
|
||||
)
|
||||
INSTALLMENTS_URL = (
|
||||
"https://bff-credit-container-portal-apigw.prd.produto-financeiro.app.grupoboticario.com.br"
|
||||
"/v1/franchisee/installments"
|
||||
)
|
||||
|
||||
_DONE_STATUSES = {"generated", "generated_csv", "done", "completed", "ready", "available"}
|
||||
_FAIL_STATUSES = {"failed", "error", "erro", "falha"}
|
||||
@ -461,6 +471,125 @@ VALUES (?, ?, ?, ?)
|
||||
return inserted
|
||||
|
||||
|
||||
# ─── ENRIQUECIMENTO paymentType ───────────────────────────────────────────────
|
||||
|
||||
def _ensure_paytype_table(cur) -> None:
|
||||
cur.execute("""
|
||||
IF OBJECT_ID('dbo.Grgb_installment_paytype', 'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.Grgb_installment_paytype (
|
||||
InstallmentGroupCode VARCHAR(50) NOT NULL PRIMARY KEY,
|
||||
PaymentType VARCHAR(80) NULL,
|
||||
ConsultadoEm DATETIME2 NOT NULL DEFAULT SYSUTCDATETIME()
|
||||
)
|
||||
END
|
||||
""")
|
||||
|
||||
|
||||
def _fetch_pending_group_codes(cur, col_name: str, file_name: str) -> list[str]:
|
||||
"""Retorna installmentGroupCodes do arquivo atual que ainda não têm paymentType."""
|
||||
cur.execute(f"""
|
||||
SELECT DISTINCT [{col_name}]
|
||||
FROM dbo.Grgb_vendas_report
|
||||
WHERE NomeArquivo = ?
|
||||
AND [{col_name}] IS NOT NULL
|
||||
AND [{col_name}] <> ''
|
||||
AND [{col_name}] NOT IN (
|
||||
SELECT InstallmentGroupCode FROM dbo.Grgb_installment_paytype
|
||||
)
|
||||
""", file_name)
|
||||
return [str(r[0]) for r in cur.fetchall()]
|
||||
|
||||
|
||||
def _query_payment_type(
|
||||
session: requests.Session,
|
||||
auth: Auth,
|
||||
group_code: str,
|
||||
) -> Optional[str]:
|
||||
"""Consulta a API de parcelas e retorna o paymentType da primeira parcela."""
|
||||
try:
|
||||
r = session.get(
|
||||
INSTALLMENTS_URL,
|
||||
params={"installmentGroupCode": group_code, "page": 1},
|
||||
headers=_headers(auth),
|
||||
timeout=30,
|
||||
)
|
||||
if not r.ok:
|
||||
print(f"[enrich] {r.status_code} para groupCode={group_code}")
|
||||
return None
|
||||
installments = r.json().get("data", {}).get("installments") or []
|
||||
if installments:
|
||||
return str(installments[0].get("paymentType") or "")
|
||||
except Exception as exc:
|
||||
print(f"[enrich] erro ao consultar groupCode={group_code}: {exc}")
|
||||
return None
|
||||
|
||||
|
||||
def enrich_payment_types(session: requests.Session, auth: Auth, file_name: str) -> None:
|
||||
"""
|
||||
Para cada installmentGroupCode do arquivo atual que ainda não consta
|
||||
em Grgb_installment_paytype, consulta a API e persiste o paymentType.
|
||||
"""
|
||||
import pyodbc
|
||||
|
||||
conn_str = _sql_conn_str()
|
||||
|
||||
cn = pyodbc.connect(conn_str, timeout=60)
|
||||
cn.autocommit = False
|
||||
cur = cn.cursor()
|
||||
_ensure_paytype_table(cur)
|
||||
cn.commit()
|
||||
|
||||
# verifica se a coluna configurada existe na tabela de dados
|
||||
cur.execute("""
|
||||
SELECT LOWER(COLUMN_NAME) FROM INFORMATION_SCHEMA.COLUMNS
|
||||
WHERE TABLE_SCHEMA='dbo' AND TABLE_NAME='Grgb_vendas_report'
|
||||
""")
|
||||
existing_cols = {r[0] for r in cur.fetchall()}
|
||||
col_name = INSTALLMENT_GROUP_COL
|
||||
if col_name.lower() not in existing_cols:
|
||||
print(
|
||||
f"[enrich] coluna '{col_name}' nao encontrada em Grgb_vendas_report. "
|
||||
f"Ajuste INSTALLMENT_GROUP_COL. Colunas disponiveis: {sorted(existing_cols)}"
|
||||
)
|
||||
cur.close(); cn.close()
|
||||
return
|
||||
|
||||
pending = _fetch_pending_group_codes(cur, col_name, file_name)
|
||||
cur.close(); cn.close()
|
||||
|
||||
total = len(pending)
|
||||
print(f"[enrich] {total} installmentGroupCode(s) para consultar")
|
||||
if not total:
|
||||
return
|
||||
|
||||
for i, group_code in enumerate(pending, 1):
|
||||
payment_type = _query_payment_type(session, auth, group_code)
|
||||
|
||||
cn = pyodbc.connect(conn_str, timeout=60)
|
||||
cn.autocommit = False
|
||||
cur = cn.cursor()
|
||||
try:
|
||||
cur.execute(
|
||||
"INSERT INTO dbo.Grgb_installment_paytype (InstallmentGroupCode, PaymentType) "
|
||||
"VALUES (?, ?)",
|
||||
group_code, payment_type,
|
||||
)
|
||||
cn.commit()
|
||||
except Exception:
|
||||
cn.rollback()
|
||||
raise
|
||||
finally:
|
||||
cur.close(); cn.close()
|
||||
|
||||
if i % 50 == 0 or i == total:
|
||||
print(f"[enrich] {i}/{total} ultimo={group_code} paymentType={payment_type}")
|
||||
|
||||
time.sleep(ENRICH_RATE_LIMIT_S)
|
||||
|
||||
print(f"[enrich] concluido: {total} registros gravados em Grgb_installment_paytype")
|
||||
|
||||
|
||||
# ─── MAIN ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
def _resolve_date_range() -> tuple[str, str]:
|
||||
@ -544,6 +673,9 @@ def main() -> None:
|
||||
else:
|
||||
n = _import_csv(rows, csv_cols, file_name, chunk_start, chunk_end)
|
||||
total_linhas += n
|
||||
if ENRICH_PAYMENT_TYPES and n > 0:
|
||||
print(f"[enrich] enriquecendo paymentType para arquivo {file_name}...")
|
||||
enrich_payment_types(session, auth, file_name)
|
||||
|
||||
if INCREMENTAL:
|
||||
_save_watermark(chunk_end)
|
||||
|
||||
1045
test_installment.py
Normal file
1045
test_installment.py
Normal file
File diff suppressed because it is too large
Load Diff
1
vendas_watermark.json
Normal file
1
vendas_watermark.json
Normal file
@ -0,0 +1 @@
|
||||
{"last_end_date": "2026-05-24"}
|
||||
Loading…
x
Reference in New Issue
Block a user