Como detectar e corrigir inconsistências de SKU duplicados entre WooCommerce e ERP sem perder pedidos
Por Kevin Marcondes · Publicado em · 8 min de leitura
SKU duplicado entre WooCommerce e ERP causa pedidos perdidos, itens errados e divergência de estoque. Veja como detectar e corrigir inconsistências de SKU duplicados entre WooCommerce e ERP sem perder pedidos: diagnóstico SQL, critérios de decisão e scripts de ajuste, com um plano seguro que mantém a operação rodando.
Sinais de SKUs duplicados e riscos reais
Duplicidade de SKU quase sempre se manifesta em momentos críticos: fechamento de caixa, integração de marketplace ou virada de campanha. Identificar cedo reduz impacto em pedidos e estoque.
Veja indícios práticos que apontam para conflito de SKUs e por que eles acontecem.
- Pedidos faturados com item divergente do carrinho (SKU A no pedido, mas SKU B foi separado).
- Estoque negativo ou overselling após integrações por lote do ERP.
- Pedidos recusados pelo ERP por “produto inexistente” mesmo com SKU visível na loja.
- Relatórios de vendas com consolidação errada por SKU (mesclando variações ou linhas distintas).
- Webhooks/ETL falhando aleatoriamente ao criar/atualizar itens por colisão de chave.
Causas técnicas mais comuns e quem manda no SKU
Antes de corrigir, defina a fonte da verdade do SKU. Sem isso, o problema tende a voltar. Observe também como o SKU é normalizado (maiúsculas/minúsculas, espaços, hífens).
Veja cenários típicos:
- ERP exporta catálogo e já possui SKUs antigos colidindo com novos.
- WooCommerce permite cadastrar variações com SKUs iguais aos do produto pai.
- Importações CSV/ETL sem validação de unicidade (case-insensitive).
- Plugins que geram SKUs automáticos sem checar conflitos.
- Diferenças de normalização: "abc-001", "ABC-001" e "abc‑001 " sendo tratados como distintos.
| Fonte mestre do SKU | Quando usar | Riscos se mal implementado |
|---|---|---|
| ERP | ERP controla catálogo, faturamento e estoque oficialmente. | Importador sem validação ou mapeamento errado de variações causa colisões. |
| WooCommerce | Catálogo nasce na loja e só depois replica ao ERP. | Integração que sobrescreve SKUs no ERP pode quebrar histórico fiscal. |
| Camada de integração | Há conciliação/ETL entre sistemas com regras de mapeamento. | Se a camada não impõe unicidade/normalização, vira outro ponto de falha. |
Diagnóstico com dados: SQL e conferência cruzada
Mapeie duplicidades no banco do WooCommerce considerando normalização (LOWER + TRIM) e status de post. Depois, cruze os resultados com relatórios do ERP para confirmar quais SKUs representam itens distintos.
Execute as consultas em uma cópia ou ambiente de staging. Faça backup antes de rodar ajustes.
-- 1) Encontrar SKUs duplicados (case-insensitive, ignorando espaços)
SELECT LOWER(TRIM(pm.meta_value)) AS sku_norm,
COUNT(*) AS total
FROM wp_postmeta pm
JOIN wp_posts p ON p.ID = pm.post_id
WHERE pm.meta_key = '_sku'
AND pm.meta_value <> ''
AND p.post_status IN ('publish','private')
GROUP BY sku_norm
HAVING COUNT(*) > 1
ORDER BY total DESC, sku_norm;
-- 2) Listar quais itens colidem por SKU normalizado
SELECT LOWER(TRIM(a.meta_value)) AS sku_norm,
a.post_id AS product_id,
p.post_type,
p.post_title
FROM wp_postmeta a
JOIN wp_posts p ON p.ID = a.post_id
WHERE a.meta_key = '_sku'
AND LOWER(TRIM(a.meta_value)) IN (
SELECT LOWER(TRIM(pm.meta_value))
FROM wp_postmeta pm
JOIN wp_posts pp ON pp.ID = pm.post_id
WHERE pm.meta_key = '_sku' AND pm.meta_value <> ''
GROUP BY LOWER(TRIM(pm.meta_value))
HAVING COUNT(*) > 1
)
ORDER BY sku_norm, product_id;
-- 3) (Opcional) Pedidos recentes impactados por SKUs duplicados
-- Observação: itens de pedido guardam meta por linha; ajuste prefixos conforme seu schema
SELECT oi.order_id,
oim_sku.meta_value AS sku_capturado,
oim_pid.meta_value AS product_id
FROM wp_woocommerce_order_items oi
JOIN wp_woocommerce_order_itemmeta oim_sku
ON oim_sku.order_item_id = oi.order_item_id AND oim_sku.meta_key = '_sku'
LEFT JOIN wp_woocommerce_order_itemmeta oim_pid
ON oim_pid.order_item_id = oi.order_item_id AND oim_pid.meta_key = '_product_id'
WHERE oim_sku.meta_value IS NOT NULL
AND LOWER(TRIM(oim_sku.meta_value)) IN (
SELECT LOWER(TRIM(pm.meta_value))
FROM wp_postmeta pm
JOIN wp_posts pp ON pp.ID = pm.post_id
WHERE pm.meta_key = '_sku' AND pm.meta_value <> ''
GROUP BY LOWER(TRIM(pm.meta_value))
HAVING COUNT(*) > 1
)
ORDER BY oi.order_id DESC
LIMIT 200;
Correção sem perder pedidos: plano seguro
Com duplicidades mapeadas, execute a correção em janelas controladas. O objetivo é preservar o vínculo dos pedidos já emitidos e impedir novas colisões enquanto ajusta o catálogo.
Siga este caminho:
- Pausar integrações que criam/atualizam produtos (jobs, webhooks, ETL).
- Escolher o SKU canônico por colisão (regra: manter o que está em pedidos recentes).
- Renomear os SKUs alternativos com sufixo técnico provisório (ex.: -ALT ou -LEGADO).
- Atualizar mapeamentos na integração/ERP para o SKU canônico (e manter alias temporário).
- Reprocessar filas apenas após validar pedidos e estoque.
- Ativar idempotência na importação de pedidos para evitar duplicação durante o catch-up.
Scripts de correção: renomear SKUs duplicados via WP-CLI
Você pode automatizar a renomeação segura de SKUs conflitados. O exemplo abaixo usa WP-CLI para: identificar colisões normalizadas, definir um canônico por SKU e renomear os demais com sufixo previsível. Teste em staging e faça backup antes.
Após a execução, valide com as consultas do diagnóstico e só então religue integrações.
<?php
/**
* wp eval-file fix-sku-dupes.php
* Requer: WP-CLI + WooCommerce carregado
*/
if (!defined('WP_CLI')) { exit; }
WP_CLI::add_command('sku:dedupe', function() {
global $wpdb;
// 1) Mapear duplicidades normalizadas
$dupes = $wpdb->get_results("SELECT LOWER(TRIM(pm.meta_value)) AS sku_norm
FROM {$wpdb->postmeta} pm
JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = '_sku' AND pm.meta_value <> ''
AND p.post_status IN ('publish','private')
GROUP BY sku_norm HAVING COUNT(*) > 1");
foreach ($dupes as $d) {
$rows = $wpdb->get_results($wpdb->prepare("SELECT pm.post_id, pm.meta_value AS sku
FROM {$wpdb->postmeta} pm
JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = '_sku' AND LOWER(TRIM(pm.meta_value)) = %s
ORDER BY p.post_date ASC", $d->sku_norm));
if (count($rows) < 2) continue;
// 2) Definir canônico (ex.: o mais antigo)
$canonical = array_shift($rows); // mantém SKU original
WP_CLI::log("Canônico para {$d->sku_norm}: #{$canonical->post_id} ({$canonical->sku})");
// 3) Renomear restantes com sufixo previsível
foreach ($rows as $r) {
$new = rtrim($canonical->sku) . '-ALT-' . $r->post_id;
update_post_meta($r->post_id, '_sku', $new);
WP_CLI::log("Renomeado #{$r->post_id} => {$new}");
}
}
WP_CLI::success('Concluído. Revise amostras e reexecute o diagnóstico.');
});
// Uso: wp sku:dedupe
Prevenção: validação, filas e idempotência
Corrigir é metade do trabalho. Para não reincidir, trate unicidade em camadas: UI/validação, integração e persistência. Aplique normalização consistente (TRIM + UPPER/LOWER) e rejeite duplicidade antes de salvar.
Você pode adicionar uma checagem simples no WooCommerce para barrar colisões durante a edição ou importação de produtos.
- Validação na aplicação (bloquear SKU duplicado ao salvar).
- Normalização consistente (case-insensitive) na leitura e escrita.
- Idempotência por chave de negócio ao criar pedidos (ex.: order_key da loja). Veja entrega eventual e idempotência entre ERP e loja.
- Processamento assíncrono confiável para catálogo e estoque; escolha uma fila para sincronização de estoque e, se preferir gerenciado, avalie opções SQS e RabbitMQ.
- Assine webhooks com HMAC para detectar alterações e garantir integridade: veja webhooks assinados (HMAC).
<?php
// Plugin mu-plugins/sku-unique-guard.php
add_action('save_post_product', 'sku_unique_guard', 10, 3);
add_action('save_post_product_variation', 'sku_unique_guard', 10, 3);
function sku_unique_guard($post_id, $post, $update) {
if (wp_is_post_revision($post_id)) return;
$sku = get_post_meta($post_id, '_sku', true);
if ($sku === '' || $sku === null) return;
global $wpdb;
$norm = strtolower(trim($sku));
$count = (int) $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->postmeta} pm
JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE pm.meta_key = '_sku'
AND LOWER(TRIM(pm.meta_value)) = %s
AND pm.post_id <> %d
AND p.post_status IN ('publish','private','draft')",
$norm, $post_id
));
if ($count > 0) {
// Impede colisão: aplica sufixo técnico e registra flag
$new = trim($sku) . '-DUP-' . $post_id;
update_post_meta($post_id, '_sku', $new);
update_post_meta($post_id, '_sku_duplicate_flag', 1);
}
}
Perguntas frequentes
Posso mudar SKUs em produtos já vendidos sem quebrar integrações?
Pode, desde que mantenha um período de alias/mapeamento na integração e não altere o SKU canônico usado por pedidos recentes. Renomeie duplicados, preserve o canônico, atualize o ERP e reprocesses depois de validar amostras em staging.
Variações podem ter o mesmo SKU do produto pai?
Tecnicamente, sim, mas é uma má prática. Trate cada variação como item único com SKU próprio. A duplicidade confunde integrações, relatórios e separação de pedidos. Aplique validação para bloquear colisões ao salvar.
SKU deve ser case-sensitive?
Para catálogos e integrações, trate SKU como case-insensitive e normalize (TRIM + UPPER/LOWER). Assim evita colisões como “abc‑001”, “ABC‑001” e “abc‑001 ”. Aplique essa regra na importação, na UI e na camada de persistência.
Como evitar perda de pedidos durante a correção?
Congele integrações, escolha o SKU canônico por colisão, renomeie os demais com sufixo técnico, valide pedidos afetados e só então religue filas. Use idempotência na criação de pedidos para não duplicar durante o reprocessamento.
Preciso de fila para sincronizar catálogo e estoque?
Filas ajudam a isolar picos e garantir ordem de eventos. Para catálogos grandes e múltiplos canais, uma fila com idempotência e reprocessamento facilita detectar colisões e manter consistência entre loja e ERP.
Quer resolver SKUs duplicados sem parar as vendas?
Vamos revisar diagnóstico, regras de normalização e sua integração (ERP, ETL, webhooks) e preparar um plano de correção seguro.
Falar sobre seu casoVeja também
- 502 Bad Gateway intermitente em Docker Compose com Nginx e PHP‑FPM: como diagnosticar e corrigir timeouts e excesso de conexões
- Como diagnosticar e capturar pacotes para intermitência de rede entre containers Docker no Debian
- Como reduzir tempo de build e evitar artefatos duplicados em CI para monólitos PHP grandes
- Como reduzir tempo de recuperação após crash de servidor Linux com playbook Ansible e runbook de recuperação