90 lines
3.1 KiB
Python
90 lines
3.1 KiB
Python
"""
|
|
Cliente da API do Tiny (Olist) — v3, OAuth2 client credentials.
|
|
|
|
ATENÇÃO Wagner: confirmar na documentação oficial antes de subir:
|
|
- o endpoint exato de marcadores do pedido
|
|
- o limite de requisições por minuto (dimensiona o worker)
|
|
- se o refresh token expira e com que frequência
|
|
Este módulo isola a API: se o endpoint mudar, muda só aqui.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import time
|
|
|
|
import httpx
|
|
|
|
BASE = os.environ.get("TINY_BASE", "https://api.tiny.com.br/public-api/v3")
|
|
CLIENT_ID = os.environ["TINY_CLIENT_ID"]
|
|
CLIENT_SECRET = os.environ["TINY_CLIENT_SECRET"]
|
|
TOKEN_URL = os.environ["TINY_TOKEN_URL"]
|
|
|
|
_token: dict = {"valor": None, "expira": 0.0}
|
|
|
|
|
|
def _acesso() -> str:
|
|
if _token["valor"] and time.time() < _token["expira"] - 60:
|
|
return _token["valor"]
|
|
r = httpx.post(TOKEN_URL, data={
|
|
"grant_type": "client_credentials",
|
|
"client_id": CLIENT_ID,
|
|
"client_secret": CLIENT_SECRET,
|
|
}, timeout=20)
|
|
r.raise_for_status()
|
|
d = r.json()
|
|
_token["valor"] = d["access_token"]
|
|
_token["expira"] = time.time() + d.get("expires_in", 3600)
|
|
return _token["valor"]
|
|
|
|
|
|
def _headers() -> dict:
|
|
return {"Authorization": f"Bearer {_acesso()}", "Content-Type": "application/json"}
|
|
|
|
|
|
def buscar_pedido(numero: str) -> dict:
|
|
r = httpx.get(f"{BASE}/pedidos", params={"numero": numero},
|
|
headers=_headers(), timeout=20)
|
|
r.raise_for_status()
|
|
itens = r.json().get("itens") or []
|
|
if not itens:
|
|
raise LookupError(f"pedido {numero} não encontrado no Tiny")
|
|
return itens[0]
|
|
|
|
|
|
def transferido_para_deposito(sku: str, deposito: str, dias: int) -> float:
|
|
"""
|
|
Soma o que foi transferido para o depósito de impressão no período.
|
|
|
|
A Altus já faz essa transferência hoje, porque também revende insumo — o
|
|
depósito separa consumo interno de revenda. Por isso o aproveitamento sai
|
|
de graça: nenhum apontamento novo na sala.
|
|
|
|
ATENÇÃO Wagner: confirmar o endpoint de movimentações de estoque por
|
|
depósito na API v3 e o nome exato do depósito no cadastro.
|
|
"""
|
|
r = httpx.get(
|
|
f"{BASE}/estoque/movimentacoes",
|
|
params={"codigo": sku, "deposito": deposito, "dias": dias, "tipo": "E"},
|
|
headers=_headers(), timeout=30,
|
|
)
|
|
r.raise_for_status()
|
|
return sum(float(m.get("quantidade", 0)) for m in r.json().get("itens", []))
|
|
|
|
|
|
def marcador(numero: str, marcador: str) -> None:
|
|
"""
|
|
Troca o marcador do pedido. Substitui os marcadores de etapa do DTF,
|
|
preservando marcadores de outra natureza (multiempresa, VALE, Troca...).
|
|
"""
|
|
pedido = buscar_pedido(numero)
|
|
atuais = [m["descricao"] for m in pedido.get("marcadores", [])]
|
|
# só três marcadores de etapa vivem no Tiny; o resto do fluxo é do kanban
|
|
ETAPAS = {"DTF-RECEBIDA", "DTF-PRODUCAO", "CORRECAO DTF", "DTF-PRONTO"}
|
|
mantem = [m for m in atuais if m not in ETAPAS]
|
|
novos = mantem + [marcador]
|
|
|
|
r = httpx.put(f"{BASE}/pedidos/{pedido['id']}/marcadores",
|
|
json={"marcadores": [{"descricao": m} for m in novos]},
|
|
headers=_headers(), timeout=20)
|
|
r.raise_for_status()
|