{
  "mapping_version": 1,
  "client": "Exemplo",
  "source": {
    "adapter": "generic",
    "note": "Exemplo de mapeamento. Copie a ESTRUTURA, nao o conteudo: os nomes de view, de coluna e de grupo sao do ERP de cada cliente.",
    "excluded_scopes": {},
    "scopes_de_teste": {
      "nota": "Escopos que nao devem ser carregados vao em `excluded_scopes` e a carga aborta neles. Classifique por VOLUME de dado, nunca pelo nome: numa base real a empresa com 'teste' na razao social era a MAIOR de todas."
    }
  },
  "defaults": {
    "key": "ID",
    "scope_column": "EMPRESA_ID",
    "chunk": 500,
    "watch_column": "ALTERADO_EM",
    "notes_watch_column": "SO PARA O OUVINTE (ouvinte.py), e opcional: sem ela o dado chega na recarga agendada, como sempre. E a coluna que a origem atualiza a cada alteracao -- e por ela que o ouvinte descobre o que mudou nos ultimos 60 segundos. Um dataset pode ter a sua propria, e `watch_column: null` no dataset DESLIGA o ao-vivo dele. Confira antes de confiar: coluna preenchida por carga em lote (todas as linhas com a mesma hora) nao serve, e coluna de granularidade de DIA faz o ouvinte reler o dia inteiro a cada ciclo.",
    "fields": {
      "ID": "Identificador do registro no ERP",
      "EMPRESA_ID": "Codigo da empresa a que o registro pertence",
      "EMPRESA_RAZAOSOCIAL": "Nome ou razao social da empresa",
      "CLIENTES_ID": "Codigo do cliente",
      "CLIENTES_RAZAOSOCIAL": "Nome ou razao social do cliente"
    }
  },
  "listener": {
    "strategy": "coluna",
    "notes": "SO PARA O OUVINTE (Etapa 10), e opcional -- ausente vale `coluna`. E a decisao de COMO detectar que houve transacao no banco, e ela nao e configuracao: e conversa com o DBA do cliente. `coluna` sonda uma coluna de alteracao e nao ve exclusao. As outras cinco -- binlog (MySQL), change_tracking (SQL Server), slot_logico (PostgreSQL), flashback (Oracle) e reconciliacao (qualquer banco, por diferenca do conjunto de chaves) -- veem exclusao e ainda NAO estao implementadas: o ouvinte as recusa dizendo o que cada uma exigiria. Rode `python3 ouvinte.py --estrategia=binlog ...` para ver a exigencia sem configurar nada."
  },
  "projects": {
    "notes": "A REGRA de o que vira um projeto no Contextia. OPCIONAL: sem este bloco, o provisionador infere como sempre fez -- o dataset com `scope_column` igual a ID e o cadastro de empresas, e a coluna de nome e a primeira com RAZAO ou NOME. Escreva-o para tirar a adivinhacao do caminho e para poder dizer QUAIS cadastros viram cliente.",
    "source_object": "VW_EMPRESAS",
    "scope_column": "ID",
    "name_column": "RAZAOSOCIAL",
    "columns": ["ID", "RAZAOSOCIAL", "CNPJ", "ATIVO", "TIPO", "HOLDING", "DT_CADASTRO"],
    "watch_column": "DT_CADASTRO",
    "include_when": { "ATIVO": ["S"] },
    "exclude_when": { "TIPO": ["T", "DEMO"] },
    "kind": "client",
    "groups": [],
    "group_column": "HOLDING",
    "group_prefix": "holding-",
    "notes_sem_logica": "`include_when` e `exclude_when` comparam IGUALDADE com uma lista de valores. Nao ha maior-que, nao ha `or`, nao ha SQL -- o mapeamento e declarativo, e um dia sera a configuracao do agente instalado. Regra que nao couber nesse formato vai para uma VIEW do cliente (uma VW_EMPRESAS_CONTEXTIA com a regra dentro), e `source_object` aponta para ela: a regra fica onde o cliente a mantem e nos continuamos so lendo.",
    "notes_exclude_vs_excluded": "`exclude_when` e regra por COLUNA e nao envelhece -- vale para empresa que ainda nao existe. `source.excluded_scopes` e lista de VALORES, um a um, e envelhece: cadastro de teste criado depois dela entra como cliente de verdade. Havendo coluna que distinga teste de producao, prefira `exclude_when`.",
    "notes_kind": "`client` e o padrao e o que todo projeto sempre foi. `source` e para projeto que carrega uma fonte do TENANT (eSocial, convenios) que nenhum cliente consome: sem a marca, ele entra na agregacao consolidada e aparece como se fosse mais uma empresa.",
    "notes_group_column": "Deriva o grupo de empresas de uma coluna do cadastro -- numa contabilidade a holding e um DADO, nao uma decisao de quem provisiona. Com ela, empresa nova de uma holding conhecida nasce no grupo certo na madrugada. `group_prefix` evita colisao de slug com outros grupos."
  },
  "datasets": [
    {
      "dataset": "empresas",
      "source_object": "VW_EMPRESAS",
      "key": "ID",
      "scope_column": "ID",
      "group": "Cadastros",
      "description": "As empresas atendidas pelo ERP",
      "columns": ["ID", "RAZAOSOCIAL", "CNPJ", "ALTERADO_EM"],
      "fields": {
        "RAZAOSOCIAL": "Nome ou razao social da empresa",
        "CNPJ": "CNPJ da empresa",
        "ALTERADO_EM": "Data e hora da ultima alteracao do cadastro no ERP"
      },
      "notes": {
        "scope": "Recorta pelo proprio ID: aqui a empresa e a linha, nao uma coluna.",
        "watch": "Herda `ALTERADO_EM` do defaults, e por isso precisa dela em `columns`. Dataset que herda a coluna e nao a tem sai do ao-vivo com o motivo no log, sem derrubar o ouvinte -- mas ai o cadastro da empresa so atualiza na varredura."
      }
    },
    {
      "dataset": "status_titulos",
      "source_object": "VW_STATUS_TITULOS",
      "key": "ID",
      "scope_column": null,
      "group": "Classificacoes",
      "description": "Dominio de situacoes de um titulo, valido para todas as empresas",
      "columns": ["ID", "DESCRICAO"],
      "watch_column": null,
      "fields": { "DESCRICAO": "Nome da situacao" },
      "notes": {
        "scope": "Tabela de dominio sem coluna de empresa: vale para todas. Um `scope_column: null` tambem e o certo quando a coluna EXISTE mas esta sempre vazia -- recortar por ela devolveria zero linhas.",
        "watch": "`watch_column: null` tira este dataset do ao-vivo: dominio nao muda, e sondar uma tabela de seis linhas a cada minuto seria consulta sem motivo na producao do cliente. Ele continua entrando na varredura diaria."
      }
    },
    {
      "dataset": "contas_receber",
      "source_object": "VW_CONTAS_RECEBER",
      "key": "ID",
      "group": "Financeiro",
      "description": "Titulos a receber, com valor, vencimento e saldo em aberto",
      "columns": [
        "ID", "EMPRESA_ID", "EMPRESA_RAZAOSOCIAL",
        "CLIENTES_ID", "CLIENTES_RAZAOSOCIAL",
        "DOCUMENTO", "DATAEMISSAO", "DATAVENCIMENTO", "VALOR", "SALDO",
        "STATUS_ID", "STATUS_DESCRICAO",
        "ALTERADO_EM"
      ],
      "fields": {
        "DOCUMENTO": "Numero do documento que originou o titulo",
        "DATAEMISSAO": "Data de emissao do titulo",
        "DATAVENCIMENTO": "Data de vencimento do titulo",
        "VALOR": "Valor original do titulo",
        "SALDO": "Saldo em aberto do titulo",
        "STATUS_ID": "Codigo da situacao do titulo no ERP",
        "STATUS_DESCRICAO": "Nome da situacao do titulo",
        "ALTERADO_EM": "Data e hora da ultima alteracao do registro no ERP"
      },
      "notes": {
        "watch": "A coluna de mudanca tem de estar em `columns`. Sem ela na projecao o ouvinte nunca acha o valor para avancar a marca de agua, e releria o mesmo intervalo indefinidamente -- por isso o ouvinte ABORTA em vez de avisar.",
        "campo_x_valor": "Descreva o CAMPO, nunca o significado de um VALOR. Aqui se escreve 'Codigo da situacao do titulo', e NAO '3 = pago'. O significado dos valores e a Etapa 8, marcado como interpretacao e com a fonte citada."
      }
    }
  ]
}
