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__))
|
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
WATERMARK_FILE = os.path.join(_SCRIPT_DIR, "vendas_watermark.json")
|
WATERMARK_FILE = os.path.join(_SCRIPT_DIR, "vendas_watermark.json")
|
||||||
IMPORT_BATCH_SIZE = 500 # linhas por lote no SQL Server
|
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 = (
|
SQL_CONN = (
|
||||||
@ -72,6 +78,10 @@ REPORTS_URL = (
|
|||||||
"https://bff-portal-apigw.produto-financeiro.grupoboticario.digital"
|
"https://bff-portal-apigw.produto-financeiro.grupoboticario.digital"
|
||||||
"/v2/franchisee/reports"
|
"/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"}
|
_DONE_STATUSES = {"generated", "generated_csv", "done", "completed", "ready", "available"}
|
||||||
_FAIL_STATUSES = {"failed", "error", "erro", "falha"}
|
_FAIL_STATUSES = {"failed", "error", "erro", "falha"}
|
||||||
@ -461,6 +471,125 @@ VALUES (?, ?, ?, ?)
|
|||||||
return inserted
|
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 ─────────────────────────────────────────────────────────────────────
|
# ─── MAIN ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
def _resolve_date_range() -> tuple[str, str]:
|
def _resolve_date_range() -> tuple[str, str]:
|
||||||
@ -544,6 +673,9 @@ def main() -> None:
|
|||||||
else:
|
else:
|
||||||
n = _import_csv(rows, csv_cols, file_name, chunk_start, chunk_end)
|
n = _import_csv(rows, csv_cols, file_name, chunk_start, chunk_end)
|
||||||
total_linhas += n
|
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:
|
if INCREMENTAL:
|
||||||
_save_watermark(chunk_end)
|
_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