Menu Produtos ganha submenu (AppShell agora suporta itens aninhados) para
cadastrar Tipos/Grupos/Marcas/Unidades; Subgrupo é gerenciado a partir da
tela do Grupo (sempre pertence a um). O cadastro de item passa a usar
dropdowns desses catálogos em vez de texto livre — exatamente o que o
comentário original da migration de products já previa como trabalho
futuro.
Backend: 5 tabelas de catálogo simples (mesmo padrão dos catálogos
fiscais), products ganha as FKs correspondentes com backfill, e
endpoints CRUD (sem exclusão física, active=false é o "apagar").
Corrige também um buraco de integridade real encontrado na integração:
PATCH /v1/products/:id usava COALESCE para os campos de catálogo
opcionais, então não dava para distinguir "não mexer" de "limpar" —
trocar o Grupo de um produto sem tocar no Subgrupo deixava o vínculo
antigo órfão e inconsistente, sem nenhuma validação pegando isso. Agora
a atualização valida o estado efetivo resultante (bloqueia com 400 em
vez de corromper o vínculo) e productSubgroupId: null limpa de verdade.
Suíte: 95/95 verdes (92→95). pnpm turbo run typecheck build: 28/28.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ADR-0006 explicitly commits to preserving the legacy's "trava" once a
contract leaves its editable draft state, overridable only by an
audited super_admin correction — but ContractsService.addItem/addParty
had no status or role check at all, letting anyone with contracts:edit
add items/parties to a contract in any status, including a signed
active one.
Adds assertEditable() (same shape as Quotes' assertNotLocked): draft
is the only free-editing state; past it, only super_admin can still
write, and that write is tagged distinctly in the audit log
(contract.item_added_override) rather than looking like an ordinary
edit. No frontend change needed — ContractDetailPage never called the
add-item/party endpoints in the first place.
Verified live: super_admin override still works past draft, and the
audit_log row for it carries overrode:true and the contract's status
at the time — confirmed directly in Postgres.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BcWWuKfykGtUweXmHD6cKx