Imported from kamilaestevam/gravity-antigravity (
skills/produtos-gravity/pedido/SKILL.md). Install upstream withnpx skills add kamilaestevam/gravity-antigravity --skill pedido. Copyright stays with the author.
Gravity — Pedido (COMEX)
O Que é o Pedido
Produto que gerencia ordens de compra/venda internacional (COMEX) com hierarquia Pedido → Itens, suporte a importação e exportação, e ciclo de vida completo: rascunho → aberto → consolidado/transferido → cancelado.
Características-chave:
- Hierarquia 1:N (Pedido tem N PedidoItem)
- Multi-tenant por
id_organizacao(Mand. 04) - DDD-puro em todas as camadas (banco/back/front) — sem ACL legado
- Cascade automático Pedido→Item em campos específicos (aba Combinado)
- 57 pares diretos Pedido→Item (SSOT em
shared/mapaPropagacaoPedidoItem.ts) + 4 exclusivos da edição em massa
Localização na Arquitetura
servicos-global/produto/pedido/
├── shared/
│ ├── campos-pedido-ddd.ts ← dicionário DDD
│ ├── camposEdicaoMassa.ts ← SSOT edição em massa (blocklist + derivação)
│ ├── mapaPropagacaoPedidoItem.ts ← cascade Pedido→Item
│ └── visaoGeralResumoAggregate.ts ← agregação Insights (server + unit)
├── client/src/
│ ├── App.tsx ← rotas irmãs → <PedidosMultiView />
│ ├── components/
│ │ ├── PedidosVisualizacaoLayout.tsx
│ │ ├── PedidosVisualizacaoTabs.tsx
│ │ ├── PedidosMultiView.tsx ← keep-alive 4 visualizações
│ │ ├── pedidos-visualizacao-context.tsx
│ │ └── lista/ … modais …
│ ├── pages/
│ │ ├── PedidosVisaoGeral.tsx ← Insights (lazy no MultiView)
│ │ ├── Pedidos.tsx, PedidosDashboard.tsx, PedidosKanban.tsx
│ │ └── Configuracoes.tsx
│ └── shared/
│ ├── pedidos-prefetch.ts
│ ├── visao-geral-schemas.ts
│ └── api.ts ← pedidoVisaoGeralApi.agregado
└── server/src/routes/
└── visao-geral-agregado.ts ← GET …/visao-geral/agregado
Seletor universal (pills): seletor-universal-visualizacoes.md · API agregada: VISAO-GERAL-AGREGADO.md (incl. § KPIs fixos do topo) · Testes: TST-*-MBOTO-*
Config KPIs topo: aba
dashboard-kpiremovida (TASK-000325). Mapeamento fixo emuseDashboardTopKpiStatus.ts— ver doc acima.
GABI Insights (Fases 1–3): SSOT GABI-INSIGHTS-PERSONALIZADOS.md. Fase 2:
useTrackBehavior→POST /api/v1/pedidos/eventos-comportamento; migrationuser_behavior_events(20260628130000). Fase 3:GABI_INSIGHTS_LLM=trueopt-in; logs comtenantId/motivo. Runbook: GABI-RUNBOOK-OPS.md.
Regras Absolutas (Referências SSOT)
⚠️ Esta skill NÃO redefine regras absolutas. Apenas referencia.
| Regra | Onde mora |
|---|---|
Schema intocável (fragment.prisma → script compose-pedido-schema.ts) |
Mand. 02 |
Nomenclatura DDD (id_pedido, tipo_operacao_pedido, id_organizacao) |
ddd-nomenclatura |
Frontend label canonical PT-BR via rotulo, não t('key') |
ddd-nomenclatura REGRA 9 |
Isolamento de organização via withOrganizacao |
isolamento-organizacao |
| Sem fallback silencioso em DEV (mock que mascara API real) | Mand. 08 |
| Zod = contrato bilateral (back valida = front parseia) | Mand. 06 + 09 |
Parte 1 — Edição em Massa
Doc completo:
documentos-tecnicos/produtos-gravity/pedido/EDICAO-EM-MASSA-TECNICO.mdRegras de negócio:EDICAO-EM-MASSA-REGRAS-NEGOCIO.md
Níveis de edição (3 abas)
| Aba | O que faz | Cascade automático? |
|---|---|---|
| Combinado (default) | Edita Pedido + Item com cascade automático em pares mapeados | ✅ 61 pares |
| Pedido | Edita só Pedido — itens permanecem | ❌ |
| Item | Edita só PedidoItem dos pedidos selecionados | ❌ |
Cascade Pedido → Item — composição SSOT (~61 pares)
SSOT: shared/mapaPropagacaoPedidoItem.ts → MAPA_PROPAGACAO_PEDIDO_ITEM (57 pares diretos).
Composição: edicaoEmMassaService.ts importa o SSOT + 4 pares exclusivos da edição em massa:
- SSOT (57): identidade comercial, casas decimais, câmbio, referências, 35 datas (rascunho/proforma/invoice/consolidação/transferência), pronto/inspeção/coleta
- Exclusivos massa (4):
tipo_operacao_pedido→tipo_operacao_item+ 3× JSONnome_*→nome_*_item
Regra: campo item explícito vence sobre cascade do mesmo destino.
SSOT campos editáveis — paridade lista ↔ massa
SSOT: shared/camposEdicaoMassa.ts — deriva CAMPOS_EDICAO_MASSA_PEDIDO e CAMPOS_EDICAO_MASSA_ITEM de campos-pedido-ddd.ts com blocklist única. Inclui colunas do usuário (EAV) via prefixo coluna_usuario:<id_coluna_usuario_pedido>.
Regra: editável na lista ⇒ editável em massa. Não manter listas paralelas no modal ou no service.
Teste EMT: TST-EMT-EDICAO-EM-MASSA-LISTA-PEDIDO-000112 · pacote 5 tipos 000108–000112 · drift: drift-lista-massa.test.ts · Local :8000
Datas no modal: usar CampoCalendarioGlobal + dateToIso() — ver EDICAO-EM-MASSA-TECNICO.md §Frontend.
Campos @@unique — convenção crítica
Campos com @@unique no schema não podem ser editados em massa via substituir com >1 pedido (geraria P2002).
Hoje exposto: numero_pedido (em Pedido.@@unique([id_organizacao, numero_pedido])).
Defesa em 3 camadas:
- Frontend — Set
CAMPOS_UNIQUEemModalPedidosEdicaoMassa.tsx:- Input
disabled+ tooltip + badge quando multi-seleção - Botão "Revisar alterações" desabilitado
- Input
- Backend Zod — Set espelhado
CAMPOS_UNIQUE_PEDIDOemedicoes-em-massa-pedido.ts+superRefine - Backend try/catch P2002 — fast path
updateManyenvolvido, converte emAppError 422 UNIQUE_VIOLATION
Convenção ao expor novo campo @@unique no SSOT (camposEdicaoMassa.ts):
- Adicionar a
CAMPOS_UNIQUEno frontend (modal) - Adicionar a
CAMPOS_UNIQUE_PEDIDOno backend Zod - Sem isso, retorna 500 e ponto cego para o usuário
Tipos mistos (importação + exportação)
Padrão Pedido: AVISAR e permitir (não bloquear). Coerente com Transferir.
- Banner azul no topo do Passo 1: "Pedidos de tipos diferentes selecionados"
- Banner laranja reforçado no Passo 2: "Atenção — tipos de operação diferentes"
Não confundir com Consolidar — esse BLOQUEIA (banner vermelho + botão disabled). Operações diferentes têm padrões diferentes.
Render por tipo de campo
Tabela detalhada (texto/numero/data/select/ncm/usuario) vive em EDICAO-EM-MASSA-TECNICO.md → seção "Frontend — Modal". Esta skill referencia para evitar dívida de manutenção quando a Fase 2 (moeda/unidade/decimal) for implementada.
Auto-fill ao trocar tipo_operacao_pedido
Quando o usuário altera tipo_operacao_pedido em massa, o sistema preenche automaticamente o lado nacional (importador em IMP, exportador em EXP) com nome + CNPJ do Workspace de cada pedido. Não usa Empresa-da-Org do Cadastros.
Regras:
- Cada pedido pega o seu próprio workspace (
pedido.id_workspace) - Backend faz 1 chamada S2S batch ao Configurador (
GET /api/v1/internal/workspaces?ids=...) - Lado oposto é limpo (nome/CNPJ do tipo antigo viram NULL)
- Cascade auto-fill para
nome_*_itemnos itens - Edição manual de
nome_exportador/nome_importador/cnpj_*no mesmo batch vence sobre auto-fill - Workspace sem CNPJ → avisa+permite (não bloqueia)
- Status crítico (≠ rascunho/aberto) → banner laranja preventivo
- Configurador offline → AppError 503 (Mand. 08)
Doc completo: EDICAO-EM-MASSA-TECNICO.md §Auto-fill.
Dívidas arquiteturais sinalizadas
| Dívida | Razão |
|---|---|
ModalPedidoNovo continua usando obterEmpresaDaOrganizacao |
Refactor da criação de pedido (próxima entrega) |
Organizacao.suid_empresa_organizacao desnecessário |
Modelo Workspace=empresa supera esse conceito |
Snapshots PedidoSnapshotEmpresa capturam empresa-da-org |
Refactor do snapshot system |
| Visualização cross-workspace na Lista (Master vê todos) | Entrega arquitetural transversal — afeta todos os produtos |
Parte 2 — Lista de Pedidos
Regras de negócio:
LISTA-EDITAR-SALVAR-REGRAS-NEGOCIO.md(§0 tooltips + colunas especiais)
Técnico:LISTA-EDITAR-SALVAR-TECNICO.md(§6 tooltips — arquitetura)
Checkbox genérico:REPLICAR-PAI-EM-ITENS-TECNICO.md
Infraestrutura
Pedidos.tsx+TabelaVirtualGlobal— 99 colunas pai, 165 colunas filhoColunasPai.tsx/ColunasFilho.tsx— catálogo;renderAgregado()= valor +⚠divergência- SSOT comportamento:
columnBehaviorConfig.ts,columnAlertConfig.ts,pedidoDivergencias.ts - Tooltips de coluna:
TooltipRegrasColuna.tsx,buildTooltipRegraLista.tsx(isLinhaItemLista,tooltipNivelCelula),pillsTooltipColunaLista.ts, núcleotooltipCelulaResolver.ts— SSOT único pedido/item para título e pills; ver doc §6 e/tooltip-pedido
Colunas com regra especial (editar-salvar inline)
| Coluna | Pedido | Item | Checkbox replicar | Alerta divergência |
|---|---|---|---|---|
| TIPO DE OPERAÇÃO | Editável | Travado | ❌ (replica sempre) | ❌ |
| STATUS | Editável | Editável (UI) | ✅ | ✅ (status_divergente) |
| Nº pedido / Part Number | numero_pedido |
part_number |
N/A | PN duplicado no pedido |
TIPO DE OPERAÇÃO: replicar_em_itens forçado true em handleEditar; item sem popover (ITEM_EDITAVEL_OVERRIDE.tipo_operacao = false).
STATUS: regras 00–04 do dono; ghost status_itens_snapshot para alerta sem expandir; edição no item só em _p.status (persistência API = dívida P0).
Hooks: statusOpts e pedidos declarados antes de mapaColunasFilho / colunasComUsuario (TDZ).
Busca da Lista (2026-06-10)
- Fonte única do WHERE:
processos-core/src/services/filtro-busca-pedido.ts(montarCondicoesBuscaPedido) — consumida porGET /pedidos,GET /pedidos/lista/kpiseGET /pedidos/inicializacao. Nunca reimplemente a condição de busca em uma rota. - Cobre campos fixos +
PedidoSnapshotEmpresa.nome_empresa+ colunas do usuário por nome e conteúdo (respeitando visibilidadetodos/roles/privado). - O filtro client-side de
Pedidos.tsxespelha as mesmas condições sobre_colunas_usuario(Mand. 07 — alterar um lado exige alterar o outro). - Detalhes:
COLUNAS-USUARIO-TECNICO.md§Busca da Lista.
+ Novo → Smart Docs (TASK-000408)
SSOT cross-produto: SMART-READ-CRIAR-PEDIDO-TECNICO.md
| Aspecto | Regra |
|---|---|
| UI | BarraAcoesPedido.tsx + menu inline Pedidos.tsx; flag PRODUCT_CONFIG.features.smart_read |
| Redirect | /smart-read/lista?origem=pedido&acao=nova-leitura |
| API Pedido | POST /api/v1/pedidos/importacoes-smart-read/criar (S2S; service smartReadParaPedidoService) |
| Histórico | canal_criacao: smart_read + id_leitura em auditLog |
Parte 3 — Consolidar / Transferir / Outras Features
A consolidar.
- Consolidar: BLOQUEIA mistura importação+exportação (regra de negócio)
- Transferir: AVISA mistura mas permite (cross-tenant possível)
- Lista — Qtd. Transferida: coluna somente leitura;
reducao_simplesincrementaquantidade_cancelada_item(não transferida) — verLISTA-EDITAR-SALVAR-REGRAS-NEGOCIO.md§8C eTRANSFERIR-REGRAS-NEGOCIO.md - Ambos usam
bulkSchemas.ts—detectarTiposMistos()síncrono eassertTiposHomogeneos()(refinement Zod)
Parte 4 — Duplicar Pedido + Item (modal misto)
Aprovado por Coordenador + Líder Técnico em 2026-05-11. Implementação em
ModalPedidosDuplicar.tsx+ backendduplicarExcluirService.ts.
O botão Duplicar trata 3 cenários numa interface única:
| Seleção | Backend chama | Resultado |
|---|---|---|
| Só pedidos | POST /duplicacoes/confirmar (1×) |
N pedidos novos com todos os itens via cascade. Usuário digita o numero_pedido de cada cópia |
| Só itens | POST /duplicacoes/itens (1× por pedido pai) |
M itens duplicados dentro do(s) pedido(s) pai(s) — sequência logo abaixo do original |
| Misto (pedido + item) | Ambos em paralelo (Promise.all) |
Toast consolidado: "N pedidos e M itens duplicados" |
Regra de ordenação após duplicação
Inviolável — qualquer alteração na ordenação da Lista deve respeitar esta regra:
-
Pedido novo (criado por duplicação) → primeira linha da Lista
- Garantido pelo
orderBy: data_criacao_pedido DESCdoGET /pedidos(offset pagination,processos-core/routes/pedidos.ts:643-650) - Como
data_criacao_pedido DateTime @default(now())énow()no INSERT, o duplicado nasce no topo
- Garantido pelo
-
Item novo (duplicado dentro do mesmo pedido) → linha imediatamente abaixo do original
- Implementado via renumeração 1..N em
duplicarExcluirService.ts:duplicarItens - Para cada item duplicado: original na posição X → cópia na X+1, e itens seguintes shiftam +1
- Lista virtual de ordem é construída antes; depois um único UPDATE por item renumera
- Implementado via renumeração 1..N em
Quantidades de execução SEMPRE zeradas no item duplicado
quantidade_pronta_item, quantidade_transferida_item e quantidade_cancelada_item viram 0 no item novo.
Motivo: essas 3 colunas representam execução real (transferências para embarque, marcações de pronto, cancelamentos). Copiar literalmente criaria saldo fantasma — o item novo apareceria com "50 transferidas" sem nenhum processo de embarque correspondente.
quantidade_inicial_item e quantidade_atual_item são copiadas (o item nasce íntegro, como se fosse novo do zero).
Aviso pré-duplicação
Se algum item selecionado tem qualquer das 3 quantidades > 0, o modal mostra um banner amarelo antes do botão Duplicar explicando que esses campos serão zerados (transparência total com o usuário).
Sincronização pai↔filhos na seleção
Como a TabelaVirtualGlobal agora sincroniza pais e filhos (ver skill arquitetura/nucleo-global), marcar o checkbox de um pedido marca todos os itens dele. O modal deve filtrar duplicação dupla:
const itensFiltrados = useMemo(
() => itens.filter(it => !idsPedidosSelecionados.has(it.pedido_id)),
[itens, idsPedidosSelecionados],
)
Itens cujo pai já está em pedidos ficam de fora do duplicarItens — o cascade do pedido já cria as cópias. Sem o filtro, cada item seria duplicado 2 vezes: uma vez no pedido novo (via cascade) e outra no pedido original (via /duplicacoes/itens).
Parte 4.1 — Excluir Pedido + Item
Regras de negócio:
DUPLICAR-EXCLUIR-REGRAS-NEGOCIO.md· Técnico:DUPLICAR-EXCLUIR-TECNICO.md
Blacklist opt-out (2026-05-26)
Coluna DB excluir_status_permitidos guarda status bloqueados (nome legado — semântica invertida):
| Valor config | Comportamento |
|---|---|
[] (default) |
Todos os status permitidos — inclusive custom (pagamento_aprovado, etc.) |
['consolidado', ...] |
Apenas os listados são bloqueados |
- Backend:
normalizarStatusBloqueadosExclusao()+statusBloqueiaExclusao()emduplicarExcluirService.ts - UI Config: checkbox marcado = status bloqueado; whitelist legada (7 canônicos) migra para
[] - Rotas
/exclusoes/confirmare/exclusoes/itens: timeout 30s; renumeração paralela pós-delete de itens
Endpoints
| Seleção | Backend |
|---|---|
| Preview pedidos | POST /exclusoes/preview |
| Hard delete pedidos | POST /exclusoes/confirmar |
| Delete itens avulsos | POST /exclusoes/itens |
Parte 4.2 — Transferir — encoding de URL
Técnico:
TRANSFERIR-TECNICO.md
pedidoTransferirApi usa pid(id) = encodeURIComponent(id) em todos os segmentos :id_pedido e :id_transferencia_pedido. Obrigatório para IDs legados com / — sem encode, modal de revisão retorna 404 Rota não encontrada.
Anti-padrões proibidos
A1 — Mock fallback silencioso em DEV
// ❌ NUNCA
preview: (payload) =>
request('/api/...').catch(err => {
if (import.meta.env.DEV) return mockX(payload)
})
// ✅ Falha ruidoso
preview: (payload) =>
request('/api/...')
Mascarar erro real com mock em DEV viola Mand. 08. Caso real corrigido em 2026-05-12: preview retornava "5 itens afetados" mockado quando a API real respondia 400 — usuário não sabia.
A2 — ACL legado→DDD no backend
Sistema é DDD-puro end-to-end. Frontend envia nome exato da coluna do Prisma (incoterm_pedido, quantidade_inicial_item, etc.). Sem LEGACY_TO_DDD map. Ver ddd-nomenclatura — glossário canônico.
A3 — Editar schema.prisma diretamente
Sempre editar fragment.prisma + rodar npx tsx scripts/ativamente/compose-pedido-schema.ts + npx prisma db push.
Filtro Multi-Workspace (entrega 2026-05-13, commit 4bafb1b6)
Visão de 1 minuto
A Lista de Pedidos suporta filtro multi-workspace: usuário escolhe N workspaces no popover do header da coluna "Workspace" e vê pedidos+itens de todos juntos. Defesa em 3 camadas: UI (popover só mostra acessíveis), backend (validarMultiWorkspace via S2S → 403 com workspaces_bloqueados[]), Portão 3 (header x-id-workspace inalterado).
Contratos críticos
- Endpoint S2S:
GET /api/v1/internal/usuarios/:id/workspaces-habilitados?id_organizacao=Xno Configurador retorna{ tipo_usuario, workspaces_habilitados: string[] }. SSOT da regra de visibilidade replica/hub/init. - Helper SDK:
obterWorkspacesHabilitadosDoUsuarioem@gravity/resolver-organizacao— consumido pelos produtos para validar listas. - Query param:
GET /api/v1/pedidos?ids_workspaces=cmo1,cmo2(CSV). Sobrepõe o headerx-id-workspacequando vem com ≥1 valor. Header continua sendo validado pelo Portão 3 separadamente. - Mand. 08: ids fora da lista do usuário → 403 com
workspaces_bloqueados[]explícito. NÃO há fallback silencioso.
Regra de visibilidade (idêntica em /hub/init e endpoint S2S)
| Tipo | Workspaces visíveis |
|---|---|
| MASTER / SUPER_ADMIN / ADMIN | Todos com status_workspace='ATIVO' da org |
| PADRAO / FORNECEDOR | ATIVO AND UsuarioWorkspace.ativo_usuario_workspace=true |
FORNECEDOR pode ser cross-organização (não exige org match). Mand. 04 NÃO se aplica a PADRAO/FORNECEDOR (sem bypass).
Comportamento frontend (Lista)
- Coluna "Workspace" sempre visível na 3ª posição (após "Tipo de Operação"). Migração automática reposiciona para usuários com prefs antigas.
- Mount: filtro inicia com workspace ATIVO pré-marcado (init useEffect, UMA vez via
initializedFilterRef). - Empty selection: usuário desmarcar tudo = lista vazia (curto-circuito local, sem fetch). Coerência popover ↔ dados.
- Chip híbrido: 1-2 nomes diretos, 3+ "N selecionados". Clicável (reabre popover ancorado no chip). Tooltip
TooltipGlobalcom lista numerada.
Cache local do escopo (PR #80)
sessionStoragepor org:pedido:workspaces_escopo:{id_organizacao}— SSOT alinhado comuseEscopoWorkspacesPedidoepedido-escopo-hub.ts.- Legado: chave global
pedido:workspaces_escopo— só leitura; nunca gravar em código novo. - Hidratação: IDs fora de
idsDisponiveissão descartados antes de usar escopo (Mand. 08 — sem fallback silencioso para outra org). - Hub: modal de divergência resolve nomes via
GET /api/v1/me/workspaces, não pelo carousel do Hub.
Quando mexer aqui — checklist
- Mudou regra de visibilidade? Atualizar DOIS lugares:
/hub/initEworkspaces-habilitados-internal.ts. Dívida D11 pede extração para serviço comum. - Adicionou novo workspace status (além de ATIVO/INATIVO)? Verificar ambos endpoints + Portão 3.
- Tocou em
parseCsvQueryParamnoprocessos-core/pedidos.ts? Mantém dedup, trim, ignore vazios — invariantes do contrato. - Mudou shape do
WorkspaceDisponivelno/hub/init? Atualizar Zod schemaworkspacesDisponiveisApiem api.ts (Mand. 09 — Zod bilateral).
Anti-padrões específicos (não repetir)
- AP1: enviar
?ids_workspaces=quandoworkspacesSelecionados === [workspaceAtivo]— duplicaria trabalho do header. OehSelecaoDefaultcheca exatamente isso. - AP2: fazer fetch quando
workspacesSelecionados.length === 0— backend cairia no header e mostraria pedidos do ativo. Curto-circuito local força lista vazia. - AP3: repopular filtro automaticamente após "× Limpar" — quebra o modelo mental (usuário desmarcou de propósito). Init é UMA vez no mount.
- AP4: hardcoded "consolidar quando há N+" no chip — usar
rotulofiltroúnico (<=2 nomes / 3+ contagem). Vale para todos os filtros enum. - AP5: usar
localStorage(gravity:idOrganizacao) como fonte dox-id-organizacaoem request. A fonte única do tenant é o store (getDynamicTenantId/getApiContextlendocurrentUser.idOrganizacao).lsGet()só hidrata oinjectTenantGetter(validado contra o store live), NUNCA alimenta request direto. Motivo: o localStorage sobrevive a logout/troca de org/sessão e, na janela sem-JWT (Clerk hidratando), resolve a org errada → 404 "Organização não encontrada" intermitente. O logout limpagravity:idOrganizacao+gravity_id_organizacao+gravity_company_idemshell/hooks/useMeSync.ts. Correção 2026-06-11.
Documentos relacionados
- Técnico:
documentos-tecnicos/produtos-gravity/pedido/FILTRO-MULTI-WORKSPACE-TECNICO.md - Regras:
documentos-tecnicos/produtos-gravity/pedido/FILTRO-MULTI-WORKSPACE-REGRAS-NEGOCIO.md - Auditoria DB:
scripts/auditar-workspaces-pedidos.mjs
Helpers compartilhados (shared/)
shared/migracaoColunas.ts (refactor D12 — 2026-05-13)
Helpers puros para migração de preferências de coluna do usuário quando uma entrega adiciona/reposiciona colunas built-in:
| Helper | Caso de uso | Idempotente? |
|---|---|---|
inserirColunaAposAncora(visiveis, key, ancoras) |
Adicionar coluna NOVA nas prefs do usuário (entrega aumenta o set de colunas built-in) | ✅ Sim — se já existe, retorna no-op |
moverColunaParaAposAncora(visiveis, keyMover, keyApos) |
Reposicionar coluna EXISTENTE quando entrega muda posição padrão | ✅ Sim — se já está depois da âncora, retorna no-op |
Composição padrão (cobre ambos os casos numa migração):
const passoInserir = inserirColunaAposAncora(saved, 'nova_coluna', ['ancora1', 'ancora2'])
const passoMover = moverColunaParaAposAncora(passoInserir.resultado, 'nova_coluna', 'ancora1')
const visiveis = passoMover.resultado
const persistir = passoInserir.mudou || passoMover.mudou
Quando usar: toda vez que uma entrega adicionar/reposicionar coluna built-in no Pedido. Substitui ~40 linhas inline por 2 chamadas testadas.
Cobertura: 16 testes unitários em __tests__/migracaoColunas.test.ts cobrindo edge cases (lista vazia, âncoras ausentes, idempotência, preservação de customização do usuário).
Parte 5 — Internacionalização (i18n)
Entrega 2026-05-22 — produto 100% i18n'd em PT/EN/ES. Veja arquitetura/traducao para o pipeline geral.
Cobertura atual
- 2743 referências
t('pedido.*')nopedido/client/src— 0 missing (todas resolvem em pt.json). - 24 arquivos
.tsxconvertidos (todos os modais, formulários, lista, dashboard, kanban, configurações, smart import, snapshot, anexos, visão geral). - Paridade pt/en/es 100% nas keys de
pedido.*.
Namespaces de keys do produto
| Namespace | Origem |
|---|---|
pedido.dashboard.* |
PedidosDashboard.tsx |
pedido.kanban.* / pedido.kanban_colunas.* |
PedidosKanban + SecaoKanbanColunas |
pedido.excluir.* |
ModalPedidosExcluir |
pedido.lista.* / pedido.barra.* / pedido.coluna_pai.* / pedido.popover_filtro.* |
Pedidos.tsx + ColunasPai + barra/filtro |
pedido.visao_geral.* |
PedidosVisaoGeral.tsx |
pedido.config.* / pedido.config_colunas.* |
Configuracoes.tsx + ConfiguracaoColunas/* |
pedido.modal_novo.* / pedido.modal_item.* |
ModalPedidoNovo + ModalItemNovo |
pedido.modal_dup.* / pedido.modal_transf.* / pedido.modal_massa.* / pedido.modal_pdf.* |
modais correspondentes |
pedido.modal_col.* / pedido.card_usuario.* |
ConfiguracaoColunas/ModalNova + ConfiguracaoCards/ModalNovo |
pedido.smart_import.* / pedido.smart_preview.* |
SmartImport/* |
pedido.anexos.* / pedido.cel_anexos.* |
AnexosPainel + CelulaAnexosColuna |
pedido.drawer.* / pedido.formulario.* |
DrawerPedido + PedidoFormulario |
pedido.massa_campos.* |
rótulos de campo da edição em massa |
Proteção contra regressão
Teste unitário testes/testes-unitarios/produto-gravity/pedido/i18n-paridade.test.ts cobre:
- Toda key
t('pedido.*')no client/src tem entry empt.json - Paridade pt → en e pt → es (sem keys faltantes)
- Sem valores vazios em
pedido.* - Variáveis
{{var}}preservadas entre os 3 idiomas
Roda no CI a cada PR — adicionou key no pt sem rodar npm run translate, falha.
Items adiados (refactor de assinatura de função pendente)
Hardcoded strings que ficaram fora do escopo i18n porque a função/módulo onde vivem não recebe t: TFunction:
components/lista/ColunasFilho.tsx— funçãomapColunaUsuarioParaGTColuna(1 string)pages/Pedidos.tsx— constantesCOLUNAS_FILHO(linhas ~657-2483) ebuildMapaColunasFilho(linhas ~2486-3160), ~50 strings de metadata de colunacomponents/lista/ColunasPai.tsx— helpersrenderQtdPedido,renderAgregado(~3 strings)pages/PedidosVisaoGeral.tsx— mocks de demo de alertas (fornecedores fictícios) — esperam dados reais do backend (Mand. 05)
Quem corrigir: passar t: TFunction como parâmetro nas funções acima e atualizar todos os callers no mesmo commit (Mand. 07 — sincronia de contratos).
Anti-padrões i18n específicos do Pedido
- AP4 — Shadowing de
tem.map(t => ...): parâmetros de callback em arrays como tabs/colunas/status que usamtsobrescrevem o hook silenciosamente. Caso real corrigido em 2026-05-22 emPedidosVisaoGeral.tsxL3623. Sempre renomear o param antes de inserirt()dentro do bloco. - AP5 — Constantes module-level com labels: declarar
const TIPO_LABELS = { ... }fora do componente impede tradução. Mover para dentro do componente comouseMemo([t])OU manter keys-only e resolver viat()no render. - AP6 — Helpers exportados sem
t:mapColunaUsuario*,renderAgregadoetc. recebem dados mas nãot. Tradução exige refactor de assinatura — Item 1-3 da lista de adiados acima.
Status da skill
| Parte | Status |
|---|---|
| 1 — Edição em Massa | ✅ Consolidada |
| 2 — Lista de Pedidos | 🟡 Em evolução — editar-salvar STATUS/TOP documentado 2026-06-03 |
| 2.2 — Painéis da Lista | ✅ ListaPainelUsuarioGlobal, /lista/paineis, PedidosListaPainelBar — menu ⋮ Renomear/Excluir via portal (createPortal + position: fixed; ver PAINEL-LISTA-CONTRATO.md §5.3, PRs #610/#615) |
| 2.1 — Filtro Multi-Workspace | ✅ Consolidada (2026-05-13) |
| 3 — Consolidar / Transferir | 🟡 Placeholder — regras de negócio em docs; transferir implementado |
| 4 — Duplicar | ✅ Consolidada |
| 4.1 — Excluir (blacklist opt-out) | ✅ Consolidada (2026-05-26) |
| 4.2 — Transferir (pid URL) | ✅ Nota técnica (2026-05-26) |
| 5 — Internacionalização (i18n) | ✅ Consolidada (2026-05-22) |
Referências cruzadas
| Para | Consultar |
|---|---|
| Lista editar-salvar (STATUS, TOP, Nº) | LISTA-EDITAR-SALVAR-REGRAS-NEGOCIO.md |
| Schema composition | arquitetura/schema-composition |
| Isolamento de org | governanca/lei/isolamento-organizacao |
| DDD nomenclatura | governanca/lei/ddd-nomenclatura |
| 9 Mandamentos | governanca/lei/9-mandamentos |
| Cadastros snapshot policy (quando consumir Empresa/Moeda/NCM) | governanca/lei/cadastros-snapshot-policy |
| Segurança 5 camadas | seguranca/seguranca-5-camadas |
| UX criação de telas | ux/criacao-telas |
| Testes | testes |