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
| Arquivo | Responsabilidade |
|---|---|
features/sale/sale-signature.controller.ts | Estado e lógica de captura/assinatura de PDF |
features/sale/components/document-sign-flow/* | Orquestra os modais de assinatura |
services/pdf/pdf.service.ts | createOrderPdfBase64, gera o PDF base do pedido |
features/sale/components/loss-quotation-sheet/loss-quotation-sheet.tsx | Sheet 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";
Só 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.