Falar com o estúdioContato
Falar com o estúdio
05/7

Varejo de veículos elétricos

Catálogo, caixa e configuração fiscal

PDV Jaguar

PDV que emite NF-e, controla estoque por chassi e espelha o catálogo na loja WooCommerce

Cliente
Jaguar Mobilidade Elétrica
Categoria
PDV com integração fiscal e e-commerce
Período
abr–ago 2026
Estado
Em produção

O problema

A Jaguar vende autopropelidos, patinetes e bikes elétricas no balcão e no site. Cada unidade tem chassi e sai com NF-e modelo 55 no nome do comprador, e não havia sistema que amarrasse chassi a cliente nem emitisse a nota na hora da venda. O catálogo do site era mantido à parte do estoque físico: produto cadastrado duas vezes, estoque desencontrado. Recibo de sinal e termo de responsabilidade eram preenchidos no papel, e os XMLs do mês eram garimpados à mão para o contador.

O que foi construído

PDV em Flutter Web sobre Firebase cobrindo catálogo, venda, caixa, orçamento, ordem de serviço e documentos fiscais, com emissão de NF-e 55 via Focus NFe através de um backend Node/Express. O app nunca chama a API do WooCommerce: fala com Firestore, com callables e com o backend fiscal. A propagação de catálogo e estoque para a loja online roda em Cloud Functions, disparada por trigger do Firestore. A escolha por Firebase veio de uma restrição do cliente: não existe equipe de operação, e a loja online fora do ar não pode travar a venda no balcão.

5
documentos em PDF
Documentos que o sistema emite sozinho
Por chassi
chassi, cor e renavam
Estoque rastreado unidade a unidade
Automático
por SKU
Venda no balcão atualiza a loja online
08/05/2026
nº 3, série 1
Primeira NF-e autorizada em produção

Decisões de engenharia

01

Venda no balcão baixa o estoque do site

Finalizar a venda grava, num único batch do Firestore, a venda e o novo status das unidades vendidas por chassi. A trigger onProductUpdated recalcula o stock_quantity do WooCommerce como a contagem de unidades com status disponivel e faz upsert por SKU. O acoplamento com a loja online mora inteiramente no servidor: se o site cair, o caixa continua vendendo.

lib/data/mock_repository.dart:235-245 (batch venda + unidades) · functions-wc/index.js:151-156 e :219 (stock_quantity) · :410-426 (trigger)

02

Rateio de desconto e juros por item no XML da NF-e

A SEFAZ rejeita a nota quando a soma dos vDesc por item difere do desconto total. O payload distribui o desconto proporcionalmente ao valor bruto de cada item e joga o drift de centavos no último; o juros do crediário entra embutido no preço unitário para vProd fechar com vNF, e a última linha de pagamento é fechada contra o vNF. É o tipo de detalhe que só aparece depois de apanhar da validação da SEFAZ.

lib/services/nfe_service.dart:268-345 (fator de juros, rateio, drift e fechamento dos pagamentos)

03

Credenciais do Woo fora do app, em codebase próprio

A primeira versão chamava a REST do WooCommerce direto do Flutter, o que embarcava consumer key e secret em qualquer build web e esbarrava em CORS; virou callable mais Secret Manager, num codebase Firebase separado com deploy independente. As triggers classificam a falha: 4xx exceto 429 é erro permanente e é engolido com log, enquanto 5xx, timeout e rede levantam para o retry automático do Firebase.

firebase.json:19-31 (dois codebases) · functions-wc/index.js:12-14 (defineSecret) e :257-280 (4xx vs transitório) · git show b076f30 (versão antiga com Basic Auth no app)

04

Assinatura vira snapshot dentro do documento

A assinatura da empresa é configurada uma vez e copiada como snapshot para dentro de cada recibo de sinal e termo, então trocar a assinatura depois não altera documento já assinado. O PDF é gerado no navegador e hospedado no Cloudinary; se o upload falhar, a assinatura do cliente já foi gravada e o PDF é regerado depois, sem pedir nova assinatura.

lib/models/assinatura_empresa.dart:6-13 · lib/services/sinal_service.dart:62-77 (grava a assinatura mesmo com o upload falhando) e :110 (regerarPdf)

Stack

App
Flutter / Dart SDK ^3.6.1, Material 3 dark, build web em produção (iOS compilado localmente)
Dados e auth
Cloud Firestore (9 coleções + subcoleção sangrias, 8 delas em listeners de tempo real) e Firebase Authentication
Serverless
Cloud Functions v2, Node 20, em dois codebases separados (default + woocommerce)
Backend fiscal
Node/Express 4.21 no Render (rotas /api/v1/nfe, /nfce, /email, /contador)
Fiscal
Focus NFe (NF-e modelo 55 e NFC-e modelo 65), certificado A1, SEFAZ-PR, CSOSN 102 (Simples)
E-commerce
WooCommerce REST API (jaguarmob.com.br), upsert por SKU
Documentos
pdf ^3.10.7 + printing ^5.11.1 + signature ^5.5.0; PDFs assinados hospedados no Cloudinary
Infra
Firebase Hosting com dois targets (pdv e termos) + Google Secret Manager para as chaves Woo
Flutter WebFirebaseCloud Functions v2NF-e modelo 55Focus NFeWooCommerce RESTPDVFirestore triggers

Telas

Catálogo com foto real de cada veículo e situação de estoque
Catálogo com foto real de cada veículo e situação de estoque
Caixa do dia, veículos em estoque e ordens de serviço em aberto
Caixa do dia, veículos em estoque e ordens de serviço em aberto
Dados fiscais, certificado A1, juros do crediário e sincronia com a loja online
Dados fiscais, certificado A1, juros do crediário e sincronia com a loja online
Produtos com preço e disponibilidade por unidade
Produtos com preço e disponibilidade por unidade
Os módulos: orçamentos, vendas, sinais, OS, vendedores, XML pro contador
Os módulos: orçamentos, vendas, sinais, OS, vendedores, XML pro contador
Certificado de garantia gerado pelo sistema (dados do cliente omitidos)
Certificado de garantia gerado pelo sistema (dados do cliente omitidos)
Catálogo em PDF, exportado do próprio catálogo do PDV
Catálogo em PDF, exportado do próprio catálogo do PDV

O papel da CG Mixart

Arquitetura, full-stack e design. A CG Mixart levantou o produto do zero: mapa de telas e fluxos validado com o cliente, identidade visual derivada do logo oficial com paleta própria (JaguarColors) e dark mode como padrão, o app Flutter inteiro, as duas camadas de backend, a integração WooCommerce e todo o caminho fiscal (modelagem do payload NF-e, certificado A1, credenciamento na Focus NFe e o cadastro UPD-PR que destravou a rejeição 974 da SEFAZ-PR). Desenvolvimento praticamente solo, com participação pontual de um segundo autor.

Ressalvas e notas técnicas, verificadas no códigoabrir

Cliente: Jaguar Mobilidade Elétrica LTDA, loja de veículos elétricos em São José dos Pinhais/PR, com e-commerce em jaguarmob.com.br. Os quatro serviços em produção responderam 200 na conferência de hoje (curl): o PDV, o micro-app público de termos, o backend e a loja WooCommerce. Os dois apps web saem do mesmo codebase Flutter, por entrypoints diferentes (lib/main.dart e lib/main_termo.dart), publicados em dois targets de hosting. O app está na v1.93 (lib/version.dart, bump manual a cada deploy). O .planning/NEXT.md registra a primeira NF-e autorizada em produção em 08/05/2026 (nº 3, série 1, protocolo 141260177556151); NFC-e ficou fora de escopo por decisão do cliente. Não existe no repositório métrica de volume de vendas, usuários ou uptime. Nada disso foi afirmado acima. Nota de nomenclatura: lib/data/mock_repository.dart herdou o nome do esqueleto, mas é o repositório real: abre os listeners do Firestore e escreve em batch. Não há mock nem fixture no caminho de produção do app. Duas pendências técnicas registradas com as palavras certas: 1. Regra de Firestore aberta demais. Hoje /termos/{id} é `allow read, write: if true` (create e delete inclusive) e, como `read` em Firestore cobre get e list, dá para listar as coleções /sinais e /termos inteiras e ler todos os recibos (nome, documento, valores, assinaturas). Antes de qualquer publicação: trocar `read` por `get` nas duas e restringir create/delete de /termos a request.auth != null, como já está em /sinais (firestore.rules:5-16). 2. Exportação de XMLs pro contador ainda aponta pra homologação. A callable exportarXmlsContador (functions/index.js:1041) consulta as vendas do mês na janela de Brasília (UTC-3 fixo), baixa os XMLs da Focus, zipa em memória com archiver e envia por e-mail. Só que a base da Focus está fixada em homologação (functions/index.js:1074), e é essa função que a tela chama (exportar_xmls_screen.dart:26). Como as notas reais foram emitidas em produção, o download falha hoje; virar é trocar a constante. A versão mais rígida, com intervalo de datas e descarte de notas de homologação pelo marcador /arquivos_development/, está no backend do Render (contadorController.js), um repositório separado que não está nesta pasta. Essa versão ainda não está ligada ao app. Há também código herdado que não roda para a Jaguar: o codebase functions/ nasceu do esqueleto de um PDV anterior e trouxe endpoints de Mercado Pago e gatilhos de agregados que disparam em coleções que este app não tem. Os documentos que eles escreveriam não são lidos em lugar nenhum de lib/. O total do caixa é incrementado no cliente, no mesmo batch da sangria (mock_repository.dart:227-231). A integração com a loja online foi feita do zero, em codebase separado (functions-wc). A sincronia é unidirecional (PDV para loja online); o webhook do WordPress de volta está documentado como backlog em INTEGRACAO_WOOCOMMERCE.md, seção 13 ("Limitações e backlog"). Seguem abertos e marcados como tal no código: importação de produtos por planilha e criação automática do usuário de Auth ao cadastrar vendedor (o vendedor cadastrado não consegue logar até isso ser feito à mão). Parte do trabalho de jun–ago/2026 foi deployada mas ainda não versionada no repositório: o módulo de recibo de sinal, a assinatura da empresa, a exportação de XMLs e a configuração de juros vivem só na cópia de trabalho. Vale acertar isso antes de mostrar o repo a alguém.