modules/sync_ingest — a porta de entrada dos dados

API pelo qual o sincronizador do cliente empurra dados para o espelho. É o modo C do contrato (cliente empurra por API); o modo B (views no banco do ERP, conector puxando) ainda não existe.

Regra de ouro deste módulo

A organização vem sempre do X-Sync-Token, nunca do JWT nem da organização padrão. É a única família de rotas com essa regra — route_sync_ingest.py abre a sessão do espelho explicitamente por tenants.engine_for(org_id, "sync"), sem passar pelo get_sync_db. Se a organização não tem espelho provisionado, responde 503.

O token é comparado por SHA-256 contra ent-sync-token (não revogado) e carimba last_used_at.

Contrato

GET /sync/v1/entities

Devolve, para cada entidade alimentável: o nome, a chave de upsert e as colunas lidas por introspecção do banco real da organização (cache de 300s).

Isso é uma boa decisão: o contrato não é uma lista fixa no código — é o que aquele banco aceita hoje. Se uma migration do template acrescentar coluna, o contrato acompanha sozinho.

POST /sync/v1/{entidade}/batch

{ "records": [ { "app_id": "...", "col": "valor" } ] }
  • Chave: app_id para tudo, exceto embalagem, que usa chv_reg.
  • INSERT ... ON CONFLICT (chave) DO UPDATE SET em todas as colunas enviadas.
  • Máximo 1000 registros por lote (413 acima).
  • Coluna desconhecida é 422, não descarte silencioso. Deliberado: o cliente descobre o erro de mapeamento na hora, em vez de perder dado em silêncio.
  • Registro sem a chave: 422.

POST /sync/v1/heartbeat

Devolve {status: ok, org_id}. Não grava nada.

Estado real

O caminho feliz funciona e é o que popula a organização demo. Quatro buracos — detalhados em flx-primeira-carga, resumidos aqui:

BuracoConsequência
3 entidades sem app_id (app_estoque_chpben, app_metadata_dictionary, app_sync_control) são anunciadas e recusadas com 422Impossível ingerir. É por isso que app_sync_control está zerada e o heartbeat não deixa rastro.
32 entidades ingeríveis contra 51 tabelas no templateFaturamento, notas fiscais, títulos e dimensões não têm caminho de carga. Esses módulos só funcionam na demo.
Sem cursor por entidade e sem reconciliaçãoToda carga é cheia; registro apagado no ERP nunca é detectado.
Nenhuma tela mostra o estado/settings/sync/erp é mockup com números fictícios.

Onde isto aparece para o cliente

Em lugar nenhum, hoje. O TI do cliente precisa: receber o token por fora, descobrir o contrato chamando GET /entities na unha e mapear as colunas sem documentação por entidade. A tese do produto — “o TI do cliente integra sozinho” — depende de uma página de Integração que ainda não existe.

Lacunas

Rastreadas na fase-1-onboarding.