▶ Slides ← Módulo
Módulo 7 · Sistemas de Informação · Sistemas de Gestão e Governança Empresarial

Análise e Carga de Dados

Análise e preparação de dados para carga no SAP

Aula 2 — Material de Leitura · Prof. Afonso Brandão · 18/08/2026

Sobre este encontro

Análise e Carga de Dados · 18/08/2026 · Prof. Afonso

Objetivo de aprendizagem

Ao final do encontro, o estudante deve ser capaz de perfilar uma base legada em planilha, especificar e aplicar em Python as regras de limpeza, padronização e validação exigidas pelo objeto de migração do SAP, e emitir o arquivo de carga acompanhado do relatório de exceções e da reconciliação de totais.

Estratégia do encontro

Exposição dialogada em quatro blocos, cada um seguido da execução imediata do trecho correspondente do notebook no Google Colab sobre uma base legada com defeitos conhecidos; o encontro encerra com a produção, por grupo, do arquivo de carga e do relatório de exceções do objeto de migração escolhido no projeto do parceiro.

Estrutura do encontro

  1. Bloco 1 (30 min) — Migração de dados na implantação: escopo dos objetos, ferramentas de carga do SAP e anatomia do template de migração
  2. Bloco 2 (35 min) — Perfilagem e qualidade: dimensões de qualidade, conformidade técnica dos campos e validação contra domínios de configuração
  3. Bloco 3 (40 min) — Prática em Google Colab: ingestão da planilha multiabas, transformação, quarentena de registros e emissão do arquivo de carga
  4. Bloco 4 (15 min) — Reconciliação, ensaios de carga e cutover: critérios de aceite, tratamento de erros e governança dos dados

1. A migração no projeto de implantação

A aula anterior tratou das regras que governam a operação e de onde cada uma deve ser implementada no sistema de gestão. Esta aula trata do insumo sem o qual nenhuma regra pode ser exercida: os dados. Um sistema corretamente configurado, quando alimentado com cadastro incompleto ou incorreto, executa decisões incorretas de forma igualmente automática.

A migração de dados é frequentemente tratada como tarefa de infraestrutura, executada nos dias que antecedem a entrada em operação. Essa leitura é a origem de boa parte dos atrasos observados em implantação. A carga propriamente dita é um procedimento breve; o trabalho que a viabiliza — extrair, medir, corrigir, mapear e validar — é análise de dados, consome semanas e depende de decisões que somente a área de negócio pode tomar.

Adota-se, ao longo desta aula, o caso já apresentado na Aula 1: a distribuidora de peças de reposição industrial, que está implantando um sistema de gestão empresarial e precisa transferir, do sistema legado, o cadastro de materiais e os saldos de estoque. O extrato disponível é uma planilha com duas abas — condição frequente em empresas de médio porte, em que o sistema legado não oferece exportação estruturada.

Erro recorrente

Examinar a base legada apenas quando o ambiente de destino já está configurado. Nesse momento, os defeitos de cadastro deixam de ser problema de dados e passam a ser problema de cronograma, porque não há mais folga para as decisões de negócio que a correção exige.

Ao final da preparação, o grupo deve ser capaz de apresentar três artefatos:

  • O arquivo de carga, no formato exigido pelo objeto de migração de destino.
  • O relatório de exceções, com todo registro rejeitado e o motivo da rejeição.
  • A reconciliação, que confronta contagens e somatórios entre a origem e o destino.

A ausência de qualquer um deles impede afirmar que a carga foi bem-sucedida: sem exceções não se sabe o que ficou de fora; sem reconciliação não se sabe se o que entrou corresponde ao que existia.

2. O ciclo da migração de dados

O trabalho organiza-se em sete etapas, percorridas integralmente a cada ensaio de carga. O ciclo é iterativo: as exceções produzidas na reconciliação realimentam a limpeza.

flowchart LR A[Extração
do legado] --> B[Perfilagem
diagnóstico] B --> C[Limpeza e
enriquecimento] C --> D[Mapeamento
de-para] D --> E[Transformação
ao layout de destino] E --> F[Carga
no ambiente] F --> G[Reconciliação
e aceite] G -.->|exceções| C
EtapaProdutoResponsabilidade
ExtraçãoExtrato do sistema legado, com data e hora de corte registradas.Equipe do sistema legado, com acompanhamento do projeto.
PerfilagemDiagnóstico quantitativo por campo e por registro.Equipe de dados do projeto.
LimpezaBase corrigida e lista de decisões pendentes.Equipe de dados; decisões cabem ao dono do dado.
MapeamentoTabelas de correspondência entre valores de origem e de destino.Área de negócio, com apoio da consultoria funcional.
TransformaçãoArquivo no layout do objeto de migração.Equipe de dados.
CargaRegistros criados no ambiente, com registro de erros.Equipe funcional e técnica.
ReconciliaçãoConfronto de totais e aceite formal.Dono do dado na área de negócio.
RM-01 Toda transformação aplicada à base legada deve ser reexecutável a partir do extrato original. Correção manual no arquivo de carga, sem alteração correspondente no código, constitui perda de rastreabilidade.

A regra tem justificativa operacional. Entre o primeiro ensaio e o cutover, o extrato é gerado novamente diversas vezes, porque a operação continua a produzir dados. Correções aplicadas manualmente ao arquivo de um ensaio se perdem no ensaio seguinte, e o mesmo defeito reaparece. Apenas a correção codificada é reaplicada quando o extrato é substituído.

3. Escopo: objetos de migração e critério de corte

Chama-se objeto de migração o conjunto de dados que constitui uma entidade do sistema de destino e que é carregado como unidade: material, parceiro de negócios, saldo de estoque, título em aberto. Cada objeto possui estrutura própria, campos obrigatórios próprios e dependências próprias.

Dados mestres e dados transacionais

Os dados mestres descrevem entidades estáveis: plano de contas, centros de custo, parceiro de negócios nos papéis de cliente e de fornecedor, material com seus dados gerais e por centro, listas técnicas e condições de preço. São migrados os registros ativos; cadastros sem movimento no período de corte são candidatos a descarte, com a decisão registrada e assinada pelo dono do dado.

Os dados transacionais descrevem posições e movimentos: saldos de estoque por material e centro, títulos em aberto a receber e a pagar, pedidos de compra e de venda pendentes, saldos contábeis de abertura. A prática consolidada consiste em migrar as posições em aberto; o histórico completo permanece consultável no sistema legado, mantido em modo de consulta, ou em repositório analítico.

Consequência de cronograma

Cada objeto acrescentado ao escopo acrescenta um ciclo completo de perfilagem, transformação, ensaio e reconciliação. A ampliação de escopo sem revisão de prazo é causa frequente de atraso no cutover, e a decisão de ampliá-lo cabe ao patrocinador do projeto.

Critério de corte

O critério de corte deve ser declarado por escrito e por objeto. Exemplos aplicáveis ao caso da distribuidora:

  • Materiais com movimento nos últimos 24 meses ou com saldo de estoque diferente de zero.
  • Clientes com pedido faturado nos últimos 36 meses ou com título em aberto.
  • Títulos em aberto na data de corte, independentemente da data de emissão.
  • Saldos de estoque na data de corte, conforme inventário confirmado.

O critério reduz volume e, sobretudo, reduz esforço de correção: não se investe trabalho de análise em cadastro que não será utilizado. A contrapartida é que o critério precisa ser decidido cedo, porque condiciona todas as etapas seguintes.

4. Ferramentas de carga do SAP

O ambiente SAP oferece mais de um caminho para inserção de dados em massa. A escolha condiciona o formato exigido do arquivo, mas não altera o trabalho de preparação, que é objeto desta aula.

RecursoUso previstoEntrada esperada
Migration Cockpit
Migrate Your Data
Instrumento padrão de migração no S/4HANA, com objetos pré-configurados por área funcional e validações próprias.Planilha no modelo publicado pelo próprio objeto, ou tabelas de preparação alimentadas por carga externa.
BAPI e interfaces de serviçoCargas recorrentes, integração contínua com sistema externo, ou objetos sem cobertura pelo instrumento padrão.Estrutura de parâmetros da interface, alimentada por programa.
IDocTroca assíncrona de documentos entre sistemas, com reprocessamento de mensagens com erro.Mensagem estruturada em segmentos e campos definidos.
Entrada manual assistidaVolumes residuais e cadastros de exceção que não compensam automação.Digitação com conferência dupla e registro de quem executou.

O Migration Cockpit opera segundo dois modos de transferência, cuja disponibilidade depende da versão e do modo de implantação: transferência por arquivo, em que o modelo de planilha é preenchido e enviado, e transferência por tabelas de preparação, em que os dados são gravados em estruturas intermediárias do banco e lidos pelo instrumento. O primeiro modo é o adequado ao volume e ao contexto desta aula.

Verificação obrigatória no projeto

Confirmar com o parceiro, antes de definir o layout de saída, qual a versão do produto e qual o instrumento de carga homologado. O modelo de planilha varia entre versões e entre modos de implantação, e o layout obtido de outra fonte — apostila, repositório público, projeto anterior — costuma divergir do exigido pelo ambiente de destino.

Ferramentas de gerações anteriores, como o Legacy System Migration Workbench, ainda são encontradas em ambientes existentes, mas não constituem o caminho recomendado para novos projetos em S/4HANA. Quando surgirem em documentação do parceiro, devem ser tratadas como informação sobre o ambiente legado.

5. Anatomia do modelo de carga

O objeto de migração publica um modelo de planilha que é, simultaneamente, formulário e especificação. A leitura desse modelo antes da escrita do código evita retrabalho.

O que o modelo declara

  • Uma aba por estrutura do objeto. O objeto material, por exemplo, separa dados gerais, textos descritivos, dados por centro e dados de unidade alternativa. Cada aba é uma tabela distinta, ligada às demais pela chave do objeto.
  • Nome técnico do campo, além do rótulo em linguagem de negócio. É pelo nome técnico que o código deve se orientar, porque o rótulo varia com o idioma.
  • Obrigatoriedade: campo-chave, obrigatório ou opcional. Campo-chave identifica o registro e não admite repetição na mesma aba.
  • Tipo, comprimento máximo e formato esperado para cada campo.
Posição do cabeçalho

As primeiras linhas do modelo contêm metadados — nome técnico, rótulo, indicação de obrigatoriedade — e o número da linha em que os dados efetivamente começam varia conforme a versão. Localize essa linha programaticamente, pela ocorrência dos nomes técnicos, em vez de fixar um número no código.

RM-02 É obrigatório que a estrutura do arquivo de carga seja derivada do modelo publicado pelo objeto de migração da versão em uso, e não de exemplo obtido de outra fonte.

Para o caso da distribuidora, a estrutura simplificada adotada no notebook desta aula é a seguinte. Os nomes técnicos correspondem aos campos usuais do cadastro de material e de estoque e servem ao exercício; no projeto, devem ser substituídos pelos do modelo real do parceiro.

CampoConteúdoRegraObservação
MATNRCódigo do materialChaveAlinhado à direita com zeros quando puramente numérico.
MAKTXDescrição do materialObrigatórioComprimento máximo de 40 caracteres.
MEINSUnidade de medida básicaObrigatórioValor constante da tabela de unidades do ambiente.
MATKLGrupo de mercadoriasObrigatórioValor existente na configuração.
WERKSCentroChave por centroCentro previamente existente no ambiente.
BRGEW / NTGEWPeso bruto e peso líquidoOpcionalPeso bruto não pode ser inferior ao líquido.
LABSTQuantidade em estoque utilizávelObrigatório no objeto de estoqueValor numérico não negativo.

6. Dimensões de qualidade de dados

Entende-se qualidade de dados como um conjunto de dimensões, cada uma correspondendo a uma verificação específica e, na maior parte dos casos, programável.

DimensãoPergunta que respondeVerificação no caso da distribuidora
CompletudeOs campos obrigatórios estão preenchidos?Material sem unidade de medida básica.
UnicidadeCada objeto do mundo real aparece uma única vez?O mesmo rolamento cadastrado sob dois códigos.
ValidadeO valor pertence ao domínio admitido pelo destino?Grupo de mercadorias inexistente na configuração.
ConsistênciaOs valores concordam entre si e entre fontes?Peso bruto inferior ao peso líquido.
AcuráciaO valor corresponde ao fato do mundo real?Saldo divergente da contagem física do inventário.
Integridade referencialAs referências apontam para registros existentes?Saldo de estoque em centro ainda não cadastrado.
AtualidadeO dado reflete a situação na data de corte?Extrato gerado antes do último faturamento do dia.

Completude, unicidade, validade, consistência, integridade referencial e atualidade são decidíveis sobre a própria base, por código. A acurácia não é: verificar se o saldo registrado corresponde ao estoque efetivamente existente exige confronto com o mundo — inventário físico, confirmação com o cliente, extrato bancário. Essa distinção é operacionalmente relevante, porque delimita o que a equipe de dados pode resolver sozinha e o que exige mobilização da operação.

Como usar as dimensões

Cada dimensão vira uma coluna do relatório de perfilagem e, depois, uma função de validação no notebook. A lista de dimensões constitui o roteiro de verificação.

7. Perfilagem da base legada

Perfilagem é a medição sistemática da base antes de qualquer correção. Seu produto é quantitativo, na forma "417 dos 3.184 materiais não têm unidade de medida; 62 descrições excedem 40 caracteres; 19 códigos aparecem mais de uma vez", em lugar de juízos genéricos como "o cadastro está ruim".

O que medir

  • Por coluna: contagem de vazios, contagem de valores distintos, distribuição dos valores mais frequentes, comprimento mínimo e máximo.
  • Por padrão: conformidade a expressão regular esperada, presença de caracteres de controle e de espaços duplicados, mistura de tipos na mesma coluna, sentinelas como 0, -1, N/A e a definir.
  • Por registro: duplicidade pela chave natural, duplicidade aproximada por descrição normalizada, registros órfãos em relação a outra aba, registros fora do critério de corte.
Ordem de trabalho

A perfilagem antecede a limpeza. Transformações escritas antes da medição tendem a tratar defeitos presumidos, a omitir os efetivamente presentes e a criar a falsa impressão de que a base foi tratada.

A tabela de perfil é entregue também ao dono do dado. Ela converte a discussão sobre qualidade em discussão sobre números e produz a lista de decisões que a área de negócio precisa tomar: o que fazer com os 417 materiais sem unidade, quais dos 19 códigos duplicados permanecem, se as 62 descrições longas são abreviadas por regra ou revisadas uma a uma.

8. Conformidade técnica dos campos

Antes de qualquer avaliação de negócio, o arquivo precisa satisfazer regras de formato. A rejeição por formato é a mais comum no primeiro ensaio e a mais barata de evitar.

Códigos com zeros à esquerda

Campos de código de comprimento fixo são armazenados com alinhamento à direita e preenchimento com zeros quando o valor é puramente numérico; valores que contêm letras permanecem alinhados à esquerda. O material 100234, em um campo de 18 posições, é armazenado como 000000000000100234, ao passo que ROL-6205 permanece inalterado.

O defeito surge no extrato: ao abrir a planilha, o aplicativo interpreta o código como número e descarta os zeros iniciais. A perda é irreversível a partir do arquivo, porque não há como distinguir, depois, entre um código que tinha zeros e outro que não tinha. Daí a exigência de ler toda a planilha como texto.

Datas

Campos de data são armazenados no formato ano-mês-dia sem separadores. O extrato costuma trazer três representações simultâneas na mesma coluna: texto no formato brasileiro 12/03/2019, texto ambíguo 03/12/2019 e número serial da planilha. A conversão exige declarar o formato de origem explicitamente, e nunca depender de inferência automática.

Números decimais

Extratos brasileiros trazem milhar com ponto e decimal com vírgula. O valor 1.234,50 convertido por inferência resulta em 1.23450 ou em erro, conforme a biblioteca. A conversão deve ser explícita e o resultado deve ser verificado por somatório antes e depois.

Unidades de medida

A unidade deve corresponder a um código existente na tabela de unidades do ambiente, no idioma de manutenção. Um mesmo conceito costuma aparecer no legado sob várias grafias — UN, un, , peça, PC  —, todas designando unidade discreta. A padronização exige tabela de correspondência, tratada na seção seguinte.

Comprimento e caixa

Campos de texto possuem comprimento máximo, e campos de chave são convertidos para maiúsculas. Descrições excedentes exigem decisão do responsável pelo dado; o corte automático é vedado pela RM-03.

RM-03 É proibido truncar silenciosamente valor que exceda o comprimento do campo de destino. O registro deve ser encaminhado à quarentena com o motivo declarado, para decisão do responsável pelo dado.

A justificativa é de negócio: a descrição do material é o que o comprador e o almoxarife leem para identificar a peça. Um corte automático em 40 caracteres pode eliminar exatamente a medida que distingue duas peças semelhantes, e o efeito só aparece meses depois, na forma de compra errada.

9. Domínios e mapeamento de-para

Campos de domínio admitem apenas valores previamente configurados no ambiente de destino: unidade de medida, grupo de mercadorias, tipo de material, condição de pagamento, centro, depósito. O valor do legado raramente coincide com o do destino, e a correspondência é decisão de negócio.

Estrutura da tabela de-para

  • Valor de origem, exatamente como consta no extrato, incluindo variações de caixa e espaços.
  • Valor de destino, existente na configuração do ambiente.
  • Responsável pela decisão e data, porque a correspondência é decisão revisável.
  • Tratamento do valor não mapeado: rejeitar o registro ou atribuir valor padrão declarado.
OrigemDestinoObservação
UN, un, , peça, PCPCUnidade discreta.
KG, Kg, quiloKGMassa.
M, metro, mtMComprimento.
CX12CXExige fator de conversão declarado como unidade alternativa.
vazio, , N/ArejeitarNão há valor padrão admissível para unidade básica.
Antipadrão

Atribuir valor padrão a todo registro cujo valor de origem não esteja mapeado, para eliminar a rejeição. O procedimento produz um arquivo que carrega integralmente e uma base que descreve incorretamente a operação. A rejeição tem por finalidade provocar a decisão do responsável pelo dado.

O mapeamento é dado de projeto: vive em arquivo próprio, versionado junto ao código, e é revisado pela área de negócio. Constantes de correspondência embutidas no meio do notebook não são auditáveis nem reutilizáveis entre ensaios.

10. Duplicidade e consolidação

Duplicidade é a presença de mais de um registro para o mesmo objeto do mundo real. É comum em bases legadas mantidas por vários usuários ao longo de anos, e sua correção envolve decisão de negócio.

Detecção

  • Exata: coincidência da chave natural após normalização — código do material, documento fiscal do cliente, par material e centro.
  • Aproximada: descrições equivalentes após remoção de acentos, pontuação e espaços redundantes, ou comparação por similaridade textual. O resultado consiste em candidatos a duplicidade, sujeitos a confirmação.

Consolidação

Escolhido o registro que permanece, o critério deve ser declarado: maior completude de campos, movimentação mais recente, ou decisão explícita do dono do dado. O que se preserva depois é tão importante quanto a escolha: a tabela de correspondência entre o identificador descartado e o mantido é artefato permanente do projeto, porque documentos históricos, integrações e relatórios do legado continuam a referenciar o código antigo.

RM-04 É obrigatório que toda consolidação de registros duplicados preserve a correspondência entre o identificador de origem e o identificador mantido no sistema de destino.

11. Dependência e sequência de carga

Objetos de migração possuem dependências, e a ordem de carga decorre de restrição de integridade. Não é possível carregar saldo de estoque de um material que ainda não existe, nem título de um cliente ainda não criado.

flowchart LR CFG[Configuração
centros, depósitos, grupos] --> CTA[Plano de contas
centros de custo] CFG --> BP[Parceiro de negócios
cliente e fornecedor] CFG --> MAT[Material
dados gerais e por centro] MAT --> EST[Saldos
de estoque] BP --> TIT[Títulos
em aberto] CTA --> TIT MAT --> PED[Pedidos
em aberto] BP --> PED

Duas consequências práticas decorrem do grafo de dependências.

  • Para a validação: a verificação de integridade referencial de um objeto deve usar a lista de chaves efetivamente carregadas do objeto antecedente, e não a lista prevista. Apenas a primeira reflete o que existe no ambiente; a segunda inclui registros que podem ter sido rejeitados.
  • Para o cronograma: a falha na carga de um objeto antecedente bloqueia todos os dependentes. O ensaio percorre a sequência completa justamente para expor esse encadeamento antes da janela de cutover, quando não há tempo de reagir.

12. O notebook: ambiente e base do caso

O trabalho prático é realizado no Google Colab, que já disponibiliza pandas e openpyxl sem instalação. Cada célula desta seção e das seguintes compõe um notebook único, executável na ordem apresentada.

Preparação

Acesse colab.research.google.com, crie um notebook, renomeie-o para carga-sap-<objeto>-<grupo>.ipynb e conceda acesso de leitura ao professor. Execute a célula de verificação abaixo antes de prosseguir.

Célula 1 — verificação do ambiente
import pandas as pd
import openpyxl, sys

print('python  ', sys.version.split()[0])
print('pandas  ', pd.__version__)
print('openpyxl', openpyxl.__version__)

A base do caso

Para que o notebook seja executável por todos os grupos desde o início da aula, a célula seguinte gera o extrato legado da distribuidora. A base é sintética e contém, propositalmente, os defeitos discutidos nas seções 6 a 10: códigos sem zeros à esquerda, descrição excedente, unidades com grafias divergentes, grupo de mercadorias fora do domínio, centro inexistente, decimais no padrão brasileiro, datas em formatos misturados, duplicidade e um saldo de estoque órfão.

Célula 2 — geração do extrato legado com defeitos conhecidos
import pandas as pd

materiais = pd.DataFrame([
    # codigo   descricao                                         unidade grupo    centro peso_bruto peso_liquido dt_criacao
    ['100234', 'Rolamento rigido de esferas 6205 2RS',            'UN',  'ROLAM', '1000', '0,132', '0,128', '12/03/2019'],
    ['100235', 'Rolamento  rigido  de esferas 6206 ZZ',           'un',  'ROLAM', '1000', '0,198', '0,190', '12/03/2019'],
    ['100236', 'Correia dentada perfil HTD 8M largura 30mm passo 8mm reforcada', 'PC', 'CORRE', '1000', '0,450', '0,440', '05/07/2020'],
    ['100237', 'Graxa multiuso litio EP2 balde 20kg',             'KG',  'LUBRI', '1000', '20,500', '20,000', '18/11/2021'],
    ['100238', 'Retentor 35x52x7 nitrilica',                      'PC',  'RETEN', '2000', '0,015', '0,014', '02/02/2022'],
    ['100239', 'Mangueira hidraulica 1/2 pol R2AT',               'M',   'MANGU', '2000', '1,100', '1,050', '30/09/2022'],
    ['100240', 'Parafuso sextavado M10x50 classe 8.8 zincado',    'PÇ',  'FIXAD', '1000', '0,048', '0,046', '14/01/2023'],
    ['100241', 'Oleo hidraulico ISO VG 68 tambor 200L',           'peça','LUBRI', '3000', '180,000', '178,000', '2023-04-22'],
    ['100242', 'Sensor indutivo M12 PNP NA 4mm',                  '',    'SENSO', '2000', '0,090', '0,088', '11/08/2023'],
    ['100243', 'Valvula solenoide 3/2 vias 24VCC',               'PC',  'PNEUM', '1000', '0,320', '0,315', '27/05/2024'],
    ['100234', 'Rolamento rigido de esferas 6205 2RS',            'PC',  'ROLAM', '1000', '0,132', '0,128', '12/03/2019'],
    ['ROL-778','Rolamento conico 30206 importado',                'PC',  'XXXXX', '1000', '0,260', '0,255', '09/09/2021'],
], columns=['CODIGO','DESCRICAO','UNIDADE','GRUPO','CENTRO',
            'PESO_BRUTO','PESO_LIQUIDO','DT_CRIACAO'])

estoque = pd.DataFrame([
    ['100234', '1000', '0001', '184,000'],
    ['100235', '1000', '0001', '96,000'],
    ['100236', '1000', '0001', '12,000'],
    ['100237', '1000', '0002', '7,500'],
    ['100238', '2000', '0001', '340,000'],
    ['100239', '2000', '0001', '58,250'],
    ['100240', '1000', '0001', '2.400,000'],
    ['100241', '3000', '0001', '3,000'],
    ['100243', '1000', '0001', '0,000'],
    ['100999', '1000', '0001', '15,000'],   # material inexistente na aba Materiais
], columns=['CODIGO','CENTRO','DEPOSITO','QUANTIDADE'])

with pd.ExcelWriter('extrato_legado.xlsx', engine='openpyxl') as saida:
    materiais.to_excel(saida, sheet_name='Materiais', index=False)
    estoque.to_excel(saida, sheet_name='Estoque', index=False)

print('extrato_legado.xlsx gerado:', len(materiais), 'materiais,', len(estoque), 'saldos')
No projeto do parceiro

Substitua a célula 2 pelo carregamento do extrato real, mantendo inalterado todo o restante do notebook. Se o extrato ainda não estiver disponível, o grupo trabalha sobre esta base até obtê-lo; o código permanece o mesmo e apenas a origem dos dados é substituída.

13. Ingestão e perfilagem

A ingestão obedece a duas exigências que decorrem diretamente da seção 8: ler tudo como texto e não permitir conversões automáticas de valores ausentes.

Célula 3 — ingestão de todas as abas
# dtype=str preserva zeros à esquerda e evita que códigos virem números.
# keep_default_na=False mantém 'NA' e 'N/A' como texto, para que a
# perfilagem possa distinguir campo vazio de campo preenchido com sentinela.
abas = pd.read_excel('extrato_legado.xlsx', sheet_name=None,
                     dtype=str, keep_default_na=False)

for nome, df in abas.items():
    print(f'{nome:<12} {df.shape[0]:>5} linhas  {df.shape[1]:>3} colunas')

materiais = abas['Materiais'].copy()
estoque   = abas['Estoque'].copy()

TOTAL_MATERIAIS = len(materiais)   # guardado para a reconciliação
TOTAL_ESTOQUE   = len(estoque)
Célula 4 — perfilagem por coluna
def perfilar(df):
    """Uma linha por coluna: vazios, cardinalidade, comprimento e exemplo."""
    linhas = []
    for col in df.columns:
        s = df[col].astype(str).str.strip()
        nao_vazios = s[s != '']
        linhas.append({
            'campo': col,
            'vazios': int((s == '').sum()),
            'distintos': int(s.nunique()),
            'compr_min': int(nao_vazios.str.len().min()) if len(nao_vazios) else 0,
            'compr_max': int(nao_vazios.str.len().max()) if len(nao_vazios) else 0,
            'exemplo': nao_vazios.iloc[0] if len(nao_vazios) else '',
        })
    return pd.DataFrame(linhas)

display(perfilar(materiais))
Célula 5 — perfilagem dos campos de domínio e da chave
# distribuição dos valores de cada campo de domínio
for campo in ['UNIDADE', 'GRUPO', 'CENTRO']:
    print(f'\n--- {campo} ---')
    print(materiais[campo].str.strip().value_counts(dropna=False))

# duplicidade pela chave natural
dups = materiais[materiais.duplicated(subset=['CODIGO'], keep=False)]
print('\ncódigos duplicados:', dups['CODIGO'].nunique(),
      '| linhas envolvidas:', len(dups))
display(dups)

# integridade referencial entre as duas abas
orfaos = estoque[~estoque['CODIGO'].isin(materiais['CODIGO'])]
print('\nsaldos sem material correspondente:', len(orfaos))
display(orfaos)
Leitura do resultado

A perfilagem da base do caso deve expor: cinco grafias para a mesma unidade discreta (UN, un, PC, , peça) e uma unidade vazia; um grupo de mercadorias fora do domínio; um centro inexistente no destino; um código repetido em duas linhas; duas descrições acima de 40 caracteres; e um saldo de estoque sem material correspondente.

Cada ocorrência deve ser confirmada antes da escrita de qualquer transformação. O mesmo procedimento de confirmação aplica-se ao projeto do parceiro.

14. Transformações canônicas

Cada regra de transformação é escrita como função que recebe um valor e devolve um valor, sem depender do DataFrame. A separação permite verificar a regra com poucos casos — inclusive os de fronteira — antes de aplicá-la a milhares de registros.

Célula 6 — funções de transformação
import re
import unicodedata

def codigo_com_zeros(valor, tamanho=18):
    """Numérico: alinha à direita com zeros. Alfanumérico: mantém como está."""
    v = (valor or '').strip().upper()
    return v.zfill(tamanho) if v.isdigit() else v

def texto_limpo(valor):
    """Remove espaços redundantes e caracteres de controle. Não trunca."""
    v = unicodedata.normalize('NFC', valor or '')
    v = re.sub(r'\s+', ' ', v)
    return ''.join(c for c in v if unicodedata.category(c)[0] != 'C').strip()

def decimal_br(valor):
    """'1.234,50' -> 1234.5 ; valor inválido ou vazio -> None"""
    v = (valor or '').strip().replace('.', '').replace(',', '.')
    return float(v) if re.fullmatch(r'-?\d+(\.\d+)?', v) else None

def data_iso(valor):
    """Aceita dd/mm/aaaa e aaaa-mm-dd; devolve aaaammdd ou None."""
    v = (valor or '').strip()
    for formato in ('%d/%m/%Y', '%Y-%m-%d'):
        try:
            return pd.to_datetime(v, format=formato).strftime('%Y%m%d')
        except (ValueError, TypeError):
            continue
    return None

# verificação das regras antes da aplicação em massa
assert codigo_com_zeros('100234') == '000000000000100234'
assert codigo_com_zeros('ROL-778') == 'ROL-778'
assert texto_limpo('Rolamento  rigido  de esferas') == 'Rolamento rigido de esferas'
assert decimal_br('2.400,000') == 2400.0
assert decimal_br('n/d') is None
assert data_iso('12/03/2019') == '20190312'
assert data_iso('2023-04-22') == '20230422'
print('regras verificadas')
Célula 7 — aplicação e mapeamento de domínio
# Tabelas de-para: dado de projeto, revisado pela área de negócio.
DEPARA_UNIDADE = {
    'UN': 'PC', 'PÇ': 'PC', 'PC': 'PC', 'PEÇA': 'PC', 'PECA': 'PC',
    'KG': 'KG', 'QUILO': 'KG',
    'M': 'M', 'METRO': 'M', 'MT': 'M',
}
GRUPOS_CONFIGURADOS = {'ROLAM', 'CORRE', 'LUBRI', 'RETEN',
                       'MANGU', 'FIXAD', 'SENSO', 'PNEUM'}
CENTROS_CARREGADOS  = {'1000', '2000'}   # obtidos do ambiente de destino

def unidade_destino(valor):
    return DEPARA_UNIDADE.get(texto_limpo(valor).upper())   # None se não mapeado

materiais['MATNR'] = materiais['CODIGO'].map(codigo_com_zeros)
materiais['MAKTX'] = materiais['DESCRICAO'].map(texto_limpo)
materiais['MEINS'] = materiais['UNIDADE'].map(unidade_destino)
materiais['MATKL'] = materiais['GRUPO'].map(lambda v: texto_limpo(v).upper())
materiais['WERKS'] = materiais['CENTRO'].map(lambda v: texto_limpo(v))
materiais['BRGEW'] = materiais['PESO_BRUTO'].map(decimal_br)
materiais['NTGEW'] = materiais['PESO_LIQUIDO'].map(decimal_br)
materiais['ERSDA'] = materiais['DT_CRIACAO'].map(data_iso)

display(materiais[['CODIGO','MATNR','MAKTX','MEINS','MATKL','WERKS','BRGEW','ERSDA']])
Sobre o valor não mapeado

A função unidade_destino devolve None quando o valor de origem não consta da tabela, em vez de atribuir um padrão. A ausência é detectada na validação e produz rejeição explícita, que é o comportamento exigido pela seção 9.

15. Validação e quarentena

A validação percorre cada registro e acumula os motivos de rejeição. O registro reprovado é separado em quarentena, com o motivo em linguagem compreensível por quem não escreveu o código, porque quem decide sobre ele é o dono do dado.

Armadilha do valor ausente

Quando uma função de transformação devolve None, o valor é convertido em NaN ao ser atribuído a uma coluna numérica do DataFrame. E NaN é verdadeiro em contexto booleano: a verificação if not valor não detecta a ausência, e if valor is None tampouco, porque o objeto já não é None.

O efeito é silencioso e grave: o registro sem unidade de medida passa pela validação, entra no arquivo de carga e é rejeitado apenas pelo sistema de destino ou, em situação mais grave, aceito com valor indevido. Por isso a validação abaixo começa por uma função ausente, que trata os três casos: None, NaN e texto vazio.

Célula 8 — validação registro a registro
LIMITE_DESCRICAO = 40

def ausente(valor):
    """True para None, NaN e texto vazio.

    Necessária porque o None devolvido pelas funções de transformação é
    convertido em NaN ao entrar na coluna, e NaN é VERDADEIRO em contexto
    booleano: 'if not valor' não detecta a ausência. Ver seção 15.
    """
    return pd.isna(valor) or str(valor).strip() == ''

def validar_material(linha):
    erros = []
    if ausente(linha['MATNR']):
        erros.append('código ausente')
    if ausente(linha['MAKTX']):
        erros.append('descrição ausente')
    elif len(linha['MAKTX']) > LIMITE_DESCRICAO:
        erros.append(f'descrição com {len(linha["MAKTX"])} caracteres '
                     f'(limite {LIMITE_DESCRICAO})')
    if ausente(linha['MEINS']):
        erros.append(f'unidade "{linha["UNIDADE"]}" fora do de-para')
    if linha['MATKL'] not in GRUPOS_CONFIGURADOS:
        erros.append(f'grupo "{linha["MATKL"]}" inexistente na configuração')
    if linha['WERKS'] not in CENTROS_CARREGADOS:
        erros.append(f'centro "{linha["WERKS"]}" não carregado no destino')
    if ausente(linha['BRGEW']):
        erros.append('peso bruto inválido ou ausente')
    elif not ausente(linha['NTGEW']) and linha['BRGEW'] < linha['NTGEW']:
        erros.append('peso bruto inferior ao peso líquido')
    if ausente(linha['ERSDA']):
        erros.append('data de criação em formato não reconhecido')
    return '; '.join(erros)

materiais['erros'] = materiais.apply(validar_material, axis=1)

# duplicidade é propriedade do conjunto, não da linha: avaliada à parte
duplicados = materiais.duplicated(subset=['MATNR'], keep=False)
materiais.loc[duplicados, 'erros'] = materiais.loc[duplicados, 'erros'].apply(
    lambda e: '; '.join(filter(None, [e, 'código duplicado no extrato']))
)

aprovados  = materiais[materiais['erros'] == ''].copy()
quarentena = materiais[materiais['erros'] != ''].copy()

print(len(aprovados), 'aprovados |', len(quarentena), 'em quarentena')
assert len(aprovados) + len(quarentena) == TOTAL_MATERIAIS
display(quarentena[['CODIGO','DESCRICAO','erros']])
RM-05 É obrigatório que a soma dos registros aprovados e dos registros em quarentena seja igual ao total lido do extrato. Registro que desaparece entre a leitura e a saída indica defeito na transformação.

A verificação é feita pela asserção da célula 8. Sem ela, uma operação de junção mal formulada ou um filtro indevido pode eliminar registros sem qualquer sinal, e a perda seria detectada apenas na reconciliação ou permaneceria não detectada.

Célula 9 — validação do objeto dependente
# A integridade referencial usa as chaves EFETIVAMENTE aprovadas,
# não a lista original de materiais (ver seção 11).
MATERIAIS_CARREGADOS = set(aprovados['MATNR'])

estoque['MATNR'] = estoque['CODIGO'].map(codigo_com_zeros)
estoque['WERKS'] = estoque['CENTRO'].map(lambda v: texto_limpo(v))
estoque['LGORT'] = estoque['DEPOSITO'].map(lambda v: texto_limpo(v))
estoque['LABST'] = estoque['QUANTIDADE'].map(decimal_br)

def validar_estoque(linha):
    erros = []
    if linha['MATNR'] not in MATERIAIS_CARREGADOS:
        erros.append('material não carregado no destino')
    if linha['WERKS'] not in CENTROS_CARREGADOS:
        erros.append(f'centro "{linha["WERKS"]}" não carregado no destino')
    if ausente(linha['LABST']):
        erros.append('quantidade inválida ou ausente')
    elif linha['LABST'] < 0:
        erros.append('quantidade negativa')
    return '; '.join(erros)

estoque['erros'] = estoque.apply(validar_estoque, axis=1)
estoque_ok  = estoque[estoque['erros'] == ''].copy()
estoque_qtn = estoque[estoque['erros'] != ''].copy()

print(len(estoque_ok), 'saldos aprovados |', len(estoque_qtn), 'em quarentena')
assert len(estoque_ok) + len(estoque_qtn) == TOTAL_ESTOQUE
display(estoque_qtn[['CODIGO','CENTRO','QUANTIDADE','erros']])
Efeito em cascata, visível

Materiais rejeitados na célula 8 provocam a rejeição dos saldos correspondentes na célula 9. O encadeamento é exatamente o do grafo de dependências da seção 11, e é essa propagação que o ensaio de carga precisa expor antes do cutover.

16. Emissão do arquivo de carga

O arquivo de carga é escrito sobre o modelo publicado pelo objeto de migração, preservando o cabeçalho. A localização da linha de dados é feita por busca dos nomes técnicos, conforme a seção 5.

Célula 10 — escrita sobre o modelo do objeto
from openpyxl import load_workbook

def escrever_no_modelo(caminho_modelo, aba, df, campo_ancora, destino):
    """Escreve df sob o cabeçalho técnico do modelo, sem alterá-lo."""
    wb = load_workbook(caminho_modelo)
    ws = wb[aba]

    colunas, primeira_linha = None, None
    for linha in ws.iter_rows(min_row=1, max_row=12):
        valores = [(c.value or '') for c in linha]
        if campo_ancora in valores:
            colunas = {v: i for i, v in enumerate(valores, start=1) if v}
            primeira_linha = linha[0].row + 1
            break
    if colunas is None:
        raise ValueError(f'cabeçalho com {campo_ancora} não encontrado em {aba}')

    for deslocamento, (_, reg) in enumerate(df.iterrows()):
        for campo, coluna in colunas.items():
            if campo in reg.index:
                ws.cell(row=primeira_linha + deslocamento,
                        column=coluna, value=reg[campo])

    wb.save(destino)
    print(destino, '→', len(df), 'registros a partir da linha', primeira_linha)

Quando o modelo oficial do parceiro ainda não estiver disponível, o exercício emite a planilha diretamente, com os nomes técnicos na primeira linha. A substituição posterior pelo modelo real afeta apenas esta célula.

Célula 11 — saída do ensaio
ENSAIO = 'ensaio1'

CAMPOS_MATERIAL = ['MATNR','MAKTX','MEINS','MATKL','WERKS','BRGEW','NTGEW','ERSDA']
CAMPOS_ESTOQUE  = ['MATNR','WERKS','LGORT','LABST']

with pd.ExcelWriter(f'carga_material_{ENSAIO}.xlsx', engine='openpyxl') as saida:
    aprovados[CAMPOS_MATERIAL].to_excel(saida, sheet_name='Basic Data', index=False)
    estoque_ok[CAMPOS_ESTOQUE].to_excel(saida, sheet_name='Stock', index=False)

with pd.ExcelWriter(f'excecoes_{ENSAIO}.xlsx', engine='openpyxl') as saida:
    quarentena[['CODIGO','DESCRICAO','UNIDADE','GRUPO','CENTRO','erros']] \
        .to_excel(saida, sheet_name='Materiais', index=False)
    estoque_qtn[['CODIGO','CENTRO','DEPOSITO','QUANTIDADE','erros']] \
        .to_excel(saida, sheet_name='Estoque', index=False)

print('arquivos do', ENSAIO, 'gerados')

O nome de cada arquivo identifica o objeto e o ensaio. Os arquivos de ensaios anteriores são preservados: a comparação entre eles demonstra a evolução da qualidade da base e é a evidência que sustenta a decisão de autorizar o cutover.

17. Reconciliação e totais de controle

A reconciliação confronta origem e destino por meio de totais de controle. Sem ela, pode-se afirmar apenas que o arquivo foi aceito, afirmação que não atesta a correção dos dados.

Totais que se confrontam

  • Contagem de registros por objeto e por agrupamento relevante — centro, grupo de mercadorias, segmento de cliente.
  • Somatório dos campos quantitativos: quantidade em estoque, valor de títulos em aberto, saldo contábil.
  • Contagem de valores distintos em campos-chave.
  • Registros em quarentena, agrupados por motivo.
Célula 12 — reconciliação
origem  = estoque.assign(QTD=estoque['QUANTIDADE'].map(decimal_br)) \
                 .groupby('CENTRO')['QTD'].sum()
destino = estoque_ok.groupby('WERKS')['LABST'].sum()

conf = pd.concat([origem, destino], axis=1,
                 keys=['origem', 'destino']).fillna(0)
conf['diferenca'] = conf['destino'] - conf['origem']
display(conf)

print('\ndivergências:')
display(conf[conf['diferenca'] != 0])

print('\nquarentena por motivo:')
display(quarentena['erros'].str.split('; ').explode().value_counts())
RM-06 É obrigatório que toda divergência de reconciliação seja explicada por registro em quarentena identificado. Divergência sem explicação impede o aceite da carga, ainda que o volume envolvido seja pequeno.

Na base do caso, a reconciliação fecha no centro 2000 e apresenta divergência nos centros 1000 e 3000. Ambas são integralmente explicadas pela quarentena: no centro 1000, pelos saldos dos materiais rejeitados por descrição excedente ou por duplicidade, e pelo saldo sem material correspondente; no centro 3000, pelo centro inexistente no destino. Essa correspondência, em que cada unidade de diferença é atribuída a um registro identificado, é o que se apresenta ao dono do dado para obter o aceite.

Quanto à magnitude, cinco saldos rejeitados retiram do destino 2.614 unidades, das quais 2.400 correspondem a um único item de alto giro. A taxa de rejeição medida em registros não é proporcional ao impacto medido em quantidade, e ambas as medidas devem constar do relatório apresentado à área de negócio.

Divergência de pequeno volume exige investigação

Diferença de poucas unidades em um somatório costuma indicar registro perdido em uma junção, e não erro de arredondamento. A investigação é obrigatória: o mesmo defeito que produz diferença pequena em ensaio produz diferença grande com o volume completo do cutover.

18. Ensaios de carga e cutover

A carga definitiva constitui a repetição de um procedimento já executado e cronometrado. Os ensaios existem para que a primeira execução do procedimento não coincida com a janela em que a operação está parada.

EnsaioPropósitoCritério de saída
PrimeiroValidar o formato do arquivo e expor os defeitos estruturais da base.Arquivo aceito pelo instrumento de carga; perfil de erros quantificado.
SegundoVerificar a eficácia das correções e a integridade entre objetos dependentes.Taxa de rejeição dentro do limite acordado; reconciliação fechada nos objetos principais.
FinalReproduzir integralmente o procedimento e medir a duração de cada etapa.Janela de execução compatível com a acordada; plano de retorno testado.
CutoverExecutar a carga com o extrato da data de corte.Reconciliação assinada pelo dono do dado; autorização de entrada em operação.

A partir do segundo ensaio, mede-se também a duração de cada etapa. A janela de cutover é finita e frequentemente noturna ou de fim de semana; etapa cuja duração não foi medida é risco não dimensionado. O plano de retorno — a decisão sobre o que fazer se a carga falhar no meio — integra o ensaio final e é definido antes da janela de cutover.

Reexecução e idempotência

Carga interrompida precisa ser retomada sem duplicar o que já entrou. Verifique, antes de reexecutar, quais chaves já existem no destino e carregue apenas o complemento. A reexecução sem essa verificação é causa frequente de registros duplicados em produção.

19. Governança e antipadrões

Responsabilidade sobre o dado

  • Cada objeto de migração possui um responsável identificado na área de negócio, que decide sobre exceções e assina a reconciliação. A equipe técnica executa; não decide o conteúdo.
  • As decisões de descarte, consolidação e valor padrão são registradas com data e autor.
  • O código de transformação e as tabelas de-para são versionados no repositório do projeto, além de mantidos no ambiente de notebook.

Dados pessoais

Extratos de clientes, fornecedores e empregados contêm dados pessoais, e a proteção exigida pela Lei Geral de Proteção de Dados Pessoais alcança, além do sistema, as planilhas intermediárias, os notebooks e as pastas em que os arquivos são armazenados durante o projeto.

  • O tratamento exige base legal e finalidade declarada, também na fase de migração.
  • Ambientes de ensaio devem utilizar dados descaracterizados sempre que o teste não exigir o valor real.
  • A retenção dos extratos intermediários é limitada e declarada, com eliminação ao final do projeto.
  • O acesso aos arquivos é restrito aos integrantes da equipe de migração.
Antipadrões

Correção manual no arquivo de carga, sem alterar o código que o gera: o defeito reaparece no ensaio seguinte.

Valor padrão para eliminar rejeição: produz arquivo que carrega e base que descreve a operação de forma incorreta.

Migração do histórico completo por ausência de critério de corte: multiplica o esforço de correção sobre dados que não serão utilizados.

Carga declarada concluída sem reconciliação: atesta-se apenas a aceitação do arquivo, sem demonstração da correção dos dados.

Extrato com dados pessoais em pasta pessoal, sem controle de acesso nem prazo de eliminação.

20. Atividade e critérios de aceitação

A atividade é conduzida em aula e concluída no bloco de desenvolvimento do projeto. Seu produto é insumo direto da aula de testes e cutover no contexto de sistemas empresariais.

Roteiro

  1. Escolher um objeto de migração pertinente ao caso do parceiro — material, cliente, fornecedor, saldo de estoque ou título em aberto.
  2. Obter o extrato legado em planilha, com pelo menos duas abas relacionadas. Na indisponibilidade do extrato real, utilizar a base sintética da seção 12.
  3. Ingerir com dtype=str e produzir a tabela de perfilagem, incluindo campos de domínio, duplicidade e registros órfãos.
  4. Declarar as regras de transformação, uma função por regra, com verificação por asserção.
  5. Declarar o mapeamento de-para de pelo menos um campo de domínio, com o tratamento previsto para o valor não mapeado.
  6. Validar registro a registro, separar a quarentena e emitir o arquivo de carga.
  7. Apresentar a reconciliação de contagens e somatórios, com cada divergência atribuída a registros identificados.

Critérios de aceitação do produto

  • O notebook executa do início ao fim, sem intervenção manual, a partir do extrato original.
  • A soma de aprovados e quarentena iguala o total lido, verificada por asserção no próprio notebook.
  • Todo registro em quarentena possui motivo legível por quem não escreveu o código.
  • Nenhum valor de domínio é atribuído por omissão; valor não mapeado produz rejeição explícita.
  • Nenhum valor é truncado silenciosamente.
  • A reconciliação é apresentada como tabela, com as divergências explicadas por registros identificados.
  • Os arquivos de saída identificam o objeto e o ensaio no nome.
Entrega

Notebook no Google Colab com acesso concedido ao professor, acompanhado do arquivo de carga e do relatório de exceções gerados. O notebook é o artefato avaliado: arquivos produzidos sem o código que os gera não atendem aos critérios.

Questão de fechamento

Dos registros encaminhados à quarentena pelo grupo, quantos decorrem de defeito de formato, corrigível por código, e quantos exigem decisão de negócio? A proporção entre os dois indica quanto do prazo de migração depende da disponibilidade do dono do dado, e não da equipe técnica.

21. Referências

  • SAP — Documentação do SAP S/4HANA, seção de migração de dados e do Migration Cockpit.
  • DAMA International — DAMA-DMBOK: Data Management Body of Knowledge. 2. ed. Technics Publications, 2017. Capítulos de qualidade de dados e de integração e interoperabilidade.
  • ISO/IEC 25012 — Data Quality Model. Dimensões de qualidade de dados.
  • McKinney, W. — Python for Data Analysis. 3. ed. O'Reilly, 2022.
  • Documentação do pandasread_excel, ExcelWriter, tratamento de valores ausentes.
  • Documentação do openpyxl — leitura e escrita em planilhas preservando formatação.
  • Brasil — Lei nº 13.709/2018, Lei Geral de Proteção de Dados Pessoais.
  • Redman, T. — Data Driven: Profiting from Your Most Important Business Asset. Harvard Business Review Press, 2008.