Pular para o conteúdo principal

Assinatura Digital e Perda de Cotação

Última atualização: 16 de julho de 2026

Visão Geral

Estas são as duas sub-telas mais isoladas do fluxo: nenhuma das duas tem hoje um gatilho real na interface que as abra, embora o código por trás de cada uma esteja em estágios bem diferentes de maturidade. A assinatura digital tem uma implementação genuinamente sofisticada de geração e assinatura de PDF, mas o resultado nunca é enviado a lugar nenhum. A perda de cotação é um mock incompleto, nem o botão de confirmar tem ação.

Arquivos-chave

ArquivoResponsabilidade
features/sale/sale-signature.controller.tsEstado e lógica de captura/assinatura de PDF
features/sale/components/document-sign-flow/*Orquestra os modais de assinatura
services/pdf/pdf.service.tscreateOrderPdfBase64, gera o PDF base do pedido
features/sale/components/loss-quotation-sheet/loss-quotation-sheet.tsxSheet de perda de cotação

Assinatura digital: PDF e captura reais, envio inexistente

A ideia é confirmar a entrega ou o pedido coletando a assinatura do cliente sobre um PDF gerado na hora, com três tipos de documento previstos:

// sale-signature.controller.ts
export type DocumentSignatureKey = "pedido" | "nota" | "boleto";

pedido tem implementação de fato, nota e boleto existem apenas como chaves de estado (signatureStatus.nota/signatureStatus.boleto), sem nenhum fluxo de captura conectado a elas.

A geração do PDF base é real, via pdfService.createOrderPdfBase64, com os dados reais da venda:

async function buildOrderPdfBase64(sale?: Sale) {
return pdfService.createOrderPdfBase64({
orderNumber: sale?.NUNOTA,
customerName: sale?.NOMEPARC,
sellerName: sale?.NOMEVEND,
negotiationDate: sale?.DTNEG,
totalValue: sale?.VLRNOTA,
});
}

A captura da assinatura usa um canvas de verdade (react-native-signature-canvas), e o resultado é embutido no PDF com pdf-lib, com uma matemática cuidadosa de conversão entre as coordenadas de toque na prévia (em pixels) e as coordenadas reais do PDF (em pontos), incluindo rotação:

const fitScale = Math.min(previewWidth / pageWidth, previewHeight / pageHeight);
// ...
page.drawImage(signatureImage, {
x: drawOriginX,
y: drawOriginY,
width: signatureWidth,
height: signatureHeight,
rotate: degrees(SIGNATURE_ROTATION_DEGREES),
});

O problema aparece depois de confirmar a assinatura: o PDF assinado (confirmedSignedOrderPdfBase64) fica apenas em estado local do React. Não existe nenhuma chamada de API que envie esse resultado a algum lugar, nem gravação em SQLite ou MMKV. Se o modal for fechado, o PDF assinado se perde, a menos que seja reaberto na mesma sessão. Anexos de arquivo/foto (via expo-document-picker/expo-image-picker, reais e funcionais na captura) têm o mesmo destino: ficam em estado local, sem envio nem persistência.

Nenhum botão real abre esse fluxo

DocumentSignFlow só é montado em features/sale/sale.tsx (a listagem de vendas), condicionado a controller.documentSignModalOpen === true. Uma busca por setDocumentSignModalOpen(true) em todo o projeto não encontra nenhuma chamada real, só uma menção na documentação interna desatualizada (SALE.md). Ou seja, o fluxo está implementado, mas hoje inacessível a partir de qualquer botão real da interface, nem na listagem, nem nos detalhes da venda.

Perda de cotação: mock incompleto, nem o botão principal funciona

O sheet apresenta quatro motivos de perda (rádio buttons: "Preço Elevado", "Estoque Cheio", "Insatisfação Com Empresa", "Nunca Comprou") e um aviso de que a ação marcaria a venda como "Venda Perdida" de forma irreversível. Só que o botão "Ok" não tem nenhum manipulador de toque:

// loss-quotation-sheet.tsx
<Button testID="features-sale-components-loss-quotation-sheet-button-2" disableStyle>
<Text style={{ color: "#1F75FE", fontWeight: "500", fontSize: 14 }}>
Ok
</Text>
</Button>

Só o botão "Cancelar" tem onPress. Tocar em "Ok" hoje não faz absolutamente nada, nem fecha o modal, nem grava qualquer estado. Não existe, em lugar nenhum do projeto, uma chamada de API para registrar perda de cotação.

Assim como a assinatura, esse sheet só é montado em sale.tsx condicionado a lossQuotationSheetOpen === true, e uma busca por setLossQuotationSheetOpen(true) no projeto inteiro não encontra nenhuma chamada real, apenas a menção no SALE.md desatualizado.

Armadilhas conhecidas

Essas duas sub-telas merecem tratamento diferente se forem retomadas: a assinatura digital já tem a parte difícil pronta (captura, geração e composição de PDF), falta só reconectar um ponto de entrada na UI e implementar o envio do resultado (o próprio SALE.md antigo já cogitava um POST /vendas/{id}/assinar). A perda de cotação, por outro lado, precisa de trabalho tanto na UI (o botão "Ok" nem está implementado) quanto de uma chamada de API que hoje não existe.