Detalhes de Venda
Última atualização: 16 de julho de 2026
Visão Geral
A tela de detalhes é a única, além do compartilhamento, totalmente real e alcançável deste fluxo. Ela mostra os dados de uma venda já existente, sua linha do tempo de vinculação (orçamento → pedido → nota), e permite duplicar, editar, excluir ou exportar a venda, tudo condicionado ao status real da venda (offline ainda não sincronizada, ou já integrada ao ERP).
Arquivos-chave
| Arquivo | Responsabilidade |
|---|---|
app/detalhes-venda.tsx | Rota |
features/sale/subscreens/sale-details/sale-details.controller.ts | Estado e ações |
features/sale/subscreens/sale-details/sale-details.permissions.ts | Resolve o que fica habilitado |
features/sale/sale-status.utils.ts | Funções puras de status, compartilhadas com a listagem de vendas |
A tela não busca dados sozinha
A venda chega inteira via parâmetro de rota, como JSON serializado por quem navegou até aqui (normalmente a listagem de vendas):
const sale = useMemo(() => {
try {
const saleParam = Array.isArray(params.sale) ? params.sale[0] : params.sale;
if (!saleParam) return {} as OrderHeader;
const parsedSale = JSON.parse(saleParam) as OrderHeader;
if (parsedSale?.isOffline || parsedSale?.rascunho === true) return parsedSale;
return { ...parsedSale, rascunho: false, finalizado: true };
} catch { return {} as OrderHeader; }
}, [params.sale]);
Se os itens completos não vierem junto (por exemplo, quando a listagem só trouxe um resumo), a tela busca sob demanda via orderHeaderService.fetchRemoteById(order.id) antes de ações como duplicar ou editar. Duas fontes reais alimentam a tela: o catálogo de status sincronizado (saleStatusService, populado por GET /status e guardado em MMKV) e orderHeaderService para operações de busca/exclusão remota.
Um dado que a tela recebe mas nunca chega a exibir: financialItems, construído a partir do mock saleFinancialMock. A aba que renderizaria essa seção está comentada no código (// case "aprovacoes": return <ApprovalsSection ... />), então é mock sem consumidor, não algo em uso.
Timeline de vinculação
A tela monta uma navegação horizontal por "passos" a partir de sale.vinculos (por exemplo, um orçamento vinculado a um pedido, vinculado a uma nota), mais a própria venda. Esse campo vinculos vem de dentro do próprio OrderHeader real, não é dado mockado.
Permissões: offline vs. venda já integrada ao ERP
// sale-details.permissions.ts
export function resolveSaleDetailsPermissions(selectedSale, selectedSaleStatus) {
if (isErpIntegratedFinalizedSale(selectedSale)) {
const statusPermissions = resolveSaleStatusActionPermissions(selectedSaleStatus);
return {
canDuplicateOrEdit: statusPermissions.canDuplicate,
canEditSelectedSale: statusPermissions.canEdit,
canDeleteSelectedSale: statusPermissions.canDelete,
canExportSelectedSale: Boolean(selectedSale?.isOffline),
};
}
const canEditSelectedSale = canEditOrDeleteSale(selectedSale);
return {
canDuplicateOrEdit: Boolean(selectedSale),
canEditSelectedSale,
canDeleteSelectedSale: canEditSelectedSale,
canExportSelectedSale: Boolean(selectedSale?.isOffline),
};
}
Duas situações completamente diferentes:
- Venda já integrada ao ERP e finalizada (
isErpIntegratedFinalizedSale: não é rascunho, está finalizada e não é offline). Editar, duplicar e excluir passam a depender de flags vindas do próprio catálogo de status sincronizado do ERP (permDuplic/permEdit/permExc, valores"S"/"N"). Se o status da venda não constar nesse catálogo, tudo fica bloqueado por segurança. Exportar é semprefalseaqui, a exportação em texto criptografado só existe para rascunhos ainda offline. - Venda offline/rascunho, incluindo os estados locais "Aguardando Integração ERP" e "Aguardando Ajustes**:
canEditOrDeleteSalelibera edição e exclusão sempre que a venda não estiver no estado ERP-finalizado, com o comentário do próprio código deixando a regra explícita: "Toda venda em rascunho (inclusive 'Aguardando Integração ERP' e 'Aguardando Ajustes') pode ser editada/excluída. Só bloqueia a venda já finalizada/integrada." (sale-status.utils.ts).
As ações reais do controller
- Duplicar: para venda offline, usa
saleDraftService.getDraftAggregatee pergunta se o vendedor quer manter o cliente atual ou trocar; para venda remota, busca os itens viaorderHeaderService.fetchRemoteByIdse necessário e monta o payload comsaleDraftService.buildDuplicatePayloadFromRemoteOrder. - Editar: prepara um rascunho (
saleDraftService.prepareDraftForEditingpara offline,prepareRemoteOrderForEditingpara remota) e navega para/nova-venda. Pedidos vinculados não podem ser editados diretamente, a mensagem de erro orienta a usar duplicação: "Edição de pedidos vinculados ainda não é suportada. Utilize a duplicação." - Excluir: para rascunho offline,
saleDraftService.clearDraft; para venda remota, uma chamada de API real viaorderHeaderService.deleteOrderHeader(o mesmo método usado no rollback de erro de envio, documentado em conversas anteriores deste fluxo de nova venda), seguida de invalidação de queries. - Exportar: só disponível para rascunho offline, gera um
.txtcriptografado viasaleDraftService.exportDraftAsEncryptedTxt. - Compartilhar: abre o bottom sheet de compartilhamento, detalhado em Compartilhamento e Relatórios.
Armadilhas conhecidas
O campo financialItems/mock de aprovações financeiras é retornado pelo controller mas não tem seção visível na tela hoje, é resíduo de uma aba planejada e depois desativada. Ao investigar por que uma ação de editar/excluir/duplicar aparece desabilitada numa venda específica, o primeiro passo é sempre checar se ela é uma venda ERP-integrada finalizada (nesse caso a resposta está no catálogo de status sincronizado, não no código do app) ou uma venda offline (nesse caso a resposta está em canEditOrDeleteSale, quase sempre permissiva).