ADR-301: Commerce — Quote, Order, Invoice, TaxInvoice
Status: Accepted
Contexto
O ciclo financeiro B2B da MIDDAG é fragmentado: quotes criados manualmente, orders criados à mão no WooCommerce, invoices buscados no Stripe por email, NFSe emitidas uma a uma no portal ISSNet (emissor NFSe municipal, Brasília/DF). Não existe pipeline automatizado de proposta-a-pagamento. A operação dual-entity (BR + GLOBAL) com moedas BRL + USD adiciona complexidade — a conta Stripe errada significa fatura errada e reembolso. O WooCommerce é o motor de commerce mas não oferece nativamente: organizations, quotes com lifecycle, dual-entity routing, nem NFSe.
Decisão
4 Domínios no Bounded Context Commerce
| Domínio | Tipo | Responsabilidade | Fonte de Dados |
|---|---|---|---|
| Quote | CORE | Propostas comerciais com lifecycle de 8 estados | Plugin (CPT) + woocommerce-quotes (satélite) |
| Order | CORE | Registros de compra | WooCommerce (HPOS) via adapter |
| Invoice | CORE | Faturas financeiras | Stripe via adapter |
| TaxInvoice | OPTIONAL | Notas fiscais NFSe brasileiras | ISSNet (SOAP) via adapter |
Quote Lifecycle (8 estados)
draft → sent → viewed → accepted → paid → fulfilled
│
expired ←──┘ (timeout configurável)
rejected ←──── (cliente recusa)| Estado | Significado | Transição seguinte |
|---|---|---|
draft | Criado, ainda não enviado ao cliente | → sent |
sent | Enviado ao cliente (link do portal) | → viewed, expired |
viewed | Cliente abriu o quote no portal | → accepted, rejected, expired |
accepted | Cliente aceitou (aceite digital no portal) | → paid |
paid | Pagamento confirmado (Stripe/Banco Inter) | → fulfilled |
fulfilled | Entitlement provisionado | Estado terminal |
expired | Timeout sem ação do cliente | Estado terminal (re-enviável) |
rejected | Cliente recusou explicitamente | Estado terminal (re-enviável) |
refunded | Estorno processado após pagamento | Estado terminal |
Nota: O 9o estado refunded cobre o cenário excepcional de estorno pós-pagamento (paid -> refunded). O estorno cancela o WC Order e sincroniza status com HubSpot.
Automação Quote → Order → Payment → Entitlement
Pipeline de 6 etapas, maioria automatizada:
- Deal HubSpot → Quote criado (sync automático ou manual)
- Quote enviado → Cliente visualiza no portal
- Cliente aceita → WC Order criada automaticamente
- Pagamento → Stripe/Banco Inter processa (webhook confirma)
- Entitlement provisionado → Auto-provisioning por classe
- Fulfilled → Cliente notificado
Detalhes em REF-301-03.
Dual-Entity Routing (BR vs GLOBAL)
Cada transação é roteada para a entidade legal correta (MIDDAG BR ou MIDDAG GLOBAL) com base em: tax ID do cliente (CNPJ→BR, EIN/VAT→GLOBAL), billing_entity da Organization, entity tag do produto, ou moeda preferencial como fallback. Admin pode override por transação.
Detalhes em REF-301-01.
Multi-Moeda BRL + USD
Dois preços fixos por produto (preferido) ou conversão automática via faixas de câmbio configuráveis (low/medium/high). Preço BR intencionalmente mais alto que LLC (incentiva compras internacionais pela LLC). UST tem valores independentes por moeda (não apenas conversão).
Detalhes em REF-301-02.
WooCommerce Como Motor de Commerce
WooCommerce é o backbone de orders, pagamentos e subscriptions — mas invisível para o cliente final. O cliente interage via portal NextJS (REST API) ou admin UI (Inertia), nunca vê terminologia ou UI do WooCommerce. OrderAdapter traduz WC orders → domain Order. SubscriptionAdapter abstrai WC Subscriptions. InvoiceAdapter traduz Stripe invoices → domain Invoice.
Backoffice exclusivo: Nenhuma página, formulário ou interface WooCommerce é exposta ao usuário final. O acesso acontece exclusivamente via WP admin pela equipe de operações. O checkout não é WooCommerce tradicional — o pedido já existe (criado pela equipe ou por integração) e o cliente no portal apenas executa o pagamento.
WC Subscriptions resolve toda a complexidade de recorrência: proration, upgrades, downgrades e recuperação de pagamento. O SubscriptionAdapter abstrai esses mecanismos para o domínio.
Plugins satélite (externos, open-source):
| Plugin | Responsabilidade | Relação com middag-account |
|---|---|---|
woocommerce-banco-inter | Gateway Banco Inter (Pix + Boleto) | Integração via hooks WC de pagamento |
woocommerce-quotes | Entidade Quote no WC (CPT, checkout flow) | middag-account consome via hooks e API interna |
Ambos os plugins são mantidos pela MIDDAG e planejados para lançamento open-source. O plugin middag-account integra-se com eles via hooks WooCommerce — nunca acopla diretamente ao código interno dos plugins satélite.
Invoice (Adapter Stripe)
Invoice é adapter somente-leitura do Stripe. Webhooks sincronizam estado. Invoice vinculado a Organization e Entitlement (via Order). PDF do Stripe disponível para download no portal.
TaxInvoice (NFSe via ISSNet — OPTIONAL, apenas BR)
Domínio OPTIONAL — relevante apenas para entidade BR. Submissão via API SOAP ISSNet. Polling para status. XML/PDF vinculado ao Invoice de origem.
Detalhes de integração Stripe (12 webhooks, dual-account, cron sync) e ISSNet (SOAP, certificado, PDF) em REF-301-04.
Consequências
Positivas:
- Pipeline automatizado elimina criação manual de orders e setup manual pós-pagamento
- Quote com 8 estados permite analytics do pipeline (tempo até aceite, taxa de conversão)
- Dual-entity routing previne erros de conta Stripe/sistema fiscal
- WC invisível para cliente — experiência B2B profissional, não loja de e-commerce
Negativas:
- Quote lifecycle de 8 estados adiciona complexidade de máquina de estados
- Dual-entity routing requer StripeAccountResolver (não existente no tema — bloqueador #4)
- Dependência de webhooks Stripe e WC para pipeline funcionar end-to-end
- TaxInvoice com ISSNet SOAP é frágil (polling, formato XML legado)
Referências
- REF-301-01 — Dual-Entity Routing Rules
- REF-301-02 — Multi-Currency Model
- REF-301-03 — Quote-to-Entitlement Pipeline
- REF-301-04 — Invoice & TaxInvoice Spec
- ADR-202 — Entitlement (auto-provisioning downstream do pipeline)
- ADR-103 — Plugin Ecosystem (WC como dependência, satélites)
operations/02-business-lines.md§2 — Dual-entity, §5 — Receita