Repository navigation
feat(orders): cart and quote totals, computed server-side - #18
Merged
Merged
Conversation
GET /cart returned `{ items }` and nothing else, so every storefront had
to sum priceCents × quantity itself. Money arithmetic in a client is a
backend gap, not a frontend puzzle — and every store that deploys this
core has a cart with a total in it.
GET /cart gains `itemsSubtotalCents` (live catalogue prices, never a
snapshot — the cart still freezes nothing) and `itemCount` (pieces, not
lines). An empty cart answers 0 for both, never null.
POST /shipping/quote gains `itemsSubtotalCents` and, on each option,
`orderTotalCents` — the exact amount POST /orders will charge if that
option is chosen. That is what lets a checkout button render "Finalizar
pedido — R$ 522,30" before an order exists, and an e2e pins it against
the totalCents of the order actually created, because any drift between
those two numbers is a bug worth catching.
itemsSubtotalCents moved out of shipping-quote.service.ts into
src/orders/money.ts: CartService now needs it and ShippingQuoteService
already depends on CartService, so leaving it there would close a runtime
import cycle through design:paramtypes.
Both changes are additive — no existing field changes meaning.
See docs/specs/cart-totals.md.
16 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
O que muda
GET /cartdevolvia{ items }e mais nada, ePOST /shipping/quotedevolvia só a lista de opções. Qualquer vitrine tinha que somar
priceCents × quantityno navegador pra montar carrinho, revisão decheckout ou badge — aritmética de dinheiro no cliente, que este projeto
trata como buraco de backend e não como exercício de front-end.
Tudo aqui é aditivo: nenhum campo existente muda de significado,
nenhum consumidor quebra.
Por quê
Toda loja que implantar este core tem um carrinho com um total dentro. E
orderTotalCentsna cotação é o número que o botão de "Finalizar pedido"exibe antes de existir pedido — o mesmo que o
POST /ordersvaicobrar, o que torna qualquer divergência entre os dois um bug com teste
em cima.
Superfície nova
CartResponse(GET /cart, e as quatro rotas de escrita, que jádevolviam o carrinho inteiro):
{ "items": [ /* inalterado */ ], "itemsSubtotalCents": 12480, // soma de priceCents × quantity, preço VIVO "itemCount": 3 // soma das quantidades — peças, não linhas }ShippingQuoteResponse(POST /shipping/quote):{ "options": [ { "code": "padrao-brasil", "label": "Entrega padrão", "priceCents": 2990, "estimatedDays": 10, "carrier": null, "orderTotalCents": 52730 // itemsSubtotalCents + priceCents } ], "itemsSubtotalCents": 49740 // presente mesmo com options vazia }Carrinho vazio devolve
0nos dois totais — nuncanull, nunca ausente.GET /cartnão ganhou total do pedido: sem CEP não há frete, e um"total" sem frete é exatamente o número que um checkout não pode exibir.
Critérios de aceitação
Todos verdes (
docs/specs/cart-totals.md):itemsSubtotalCents: 12480,itemCount: 30nos dois, presentesDELETE /cart⇒items: []e os dois totais em0POST/PATCH/DELETEde item ⇒ totais já refletindo a mudançaGET /cartorderTotalCents == itemsSubtotalCents + priceCentsem toda opçãopriceCents: 0⇒orderTotalCents == itemsSubtotalCentsorderTotalCentsda opção escolhida ==totalCentsdo pedido criado409openapi.jsonregenerado descreve os três camposNota de implementação
itemsSubtotalCentssaiu deshipping-quote.service.tsparasrc/orders/money.ts(junto comitemCount, novo).CartServicepassoua precisar da soma e
ShippingQuoteServicejá depende deCartService:deixar a função onde estava fecharia um ciclo de import em runtime, porque
o
design:paramtypesdo construtor é referência de valor, não só de tipo.Continua havendo uma definição de subtotal, usada por carrinho, cotação
e checkout.
Verificação
pnpm lint:checkpnpm typecheckpnpm testpnpm buildpnpm test:e2eO e2e rodou contra o Supabase de desenvolvimento (
commerce-core-dev),confirmado antes de rodar — a suíte dá
TRUNCATEno banco pra ondeDATABASE_URLaponta.O caso de frete grátis é coberto no unitário e não no e2e: a tabela de
frete é lida do ambiente no boot, então uma opção gratuita exigiria subir
um segundo app com outra
SHIPPING_TABLE.Sem migration — os três números saem de dados que já estão em memória no
momento da resposta.