-- Histórico de vendas vindas do CRM. -- -- Cada venda é uma LINHA com a data em que ocorreu (sale_occurred_at). É essa -- dimensão de tempo que permite responder "faturado hoje / na semana / no mês", -- algo impossível com o contador agregado sku.sku_sold. -- -- O sku.sku_sold continua existindo como total acumulado (cache de leitura -- rápida), incrementado pela aplicação a cada nova venda registrada aqui. -- -- Isolamento: company_id é resolvido pela URL/HMAC do webhook, nunca pelo corpo. -- sku_id / client_id / operator_id são INT DEFAULT 0 (sem FK), seguindo o mesmo -- padrão adotado em integration.user_id/operator_id (migrations_v2): 0 = não -- vinculado, pois o CRM pode mandar uma venda sem produto/cliente/operador -- cadastrado na plataforma. CREATE TABLE IF NOT EXISTS sale ( sale_id SERIAL PRIMARY KEY, company_id INT NOT NULL, sku_id INT NOT NULL DEFAULT 0, client_id INT NOT NULL DEFAULT 0, operator_id INT NOT NULL DEFAULT 0, sale_external_id TEXT NOT NULL, sale_amount DECIMAL(12,2) NOT NULL, sale_quantity INT NOT NULL DEFAULT 1, sale_occurred_at TIMESTAMP NOT NULL, sale_created_at TIMESTAMP NOT NULL DEFAULT NOW(), sale_deleted_at TIMESTAMP NOT NULL DEFAULT 'infinity', CONSTRAINT fk_sale_company FOREIGN KEY (company_id) REFERENCES company(company_id) ); -- Idempotência: o CRM pode reenviar a mesma venda. A chave (company_id, -- sale_external_id) impede duplicar o faturamento. Parcial em registros ativos -- para conviver com a exclusão lógica. CREATE UNIQUE INDEX IF NOT EXISTS uq_sale_company_external_active ON sale (company_id, sale_external_id) WHERE sale_deleted_at = 'infinity'; -- Acelera as consultas de faturamento por período (dia/semana/mês). CREATE INDEX IF NOT EXISTS idx_sale_company_occurred ON sale (company_id, sale_occurred_at) WHERE sale_deleted_at = 'infinity';