'use strict' const { execute, queryOne, query } = require('../database/postgres') class Cotacao { static findById(id) { return queryOne(` SELECT c.*, COALESCE(cl.nome, u.nome) AS cliente_nome, COALESCE(cl.tipo, 'PF') AS cliente_tipo FROM cotacoes c LEFT JOIN clientes cl ON cl.id = c.cliente_id LEFT JOIN users u ON u.id = c.user_id WHERE c.id = $1 `, [id]) } static listByCliente(clienteId, tenantId) { return query( `SELECT * FROM cotacoes WHERE cliente_id = $1 AND tenant_id = $2 ORDER BY created_at DESC`, [clienteId, tenantId] ) } static listByUser(userId, tenantId) { return query( `SELECT DISTINCT ON (c.id) c.*, p.id AS pedido_id, p.protocolo AS pedido_protocolo, p.status AS pedido_status, p.forma_pagto AS pedido_forma_pagto, p.tipo_entrega AS pedido_tipo_entrega, p.financeiro_status AS pedido_financeiro_status, ( SELECT json_agg(json_build_object( 'status', sl.status, 'created_at', sl.created_at ) ORDER BY sl.created_at ASC) FROM pedido_status_log sl WHERE sl.pedido_id = p.id ) AS pedido_status_log, ( SELECT json_agg(json_build_object( 'id', pi.id, 'nome_produto', pi.nome_produto, 'sku', pi.sku, 'quantidade', pi.quantidade, 'subtotal', pi.subtotal, 'observacao', pi.observacao, 'alerta_tipo', pi.alerta_tipo, 'alerta_obs', pi.alerta_obs, 'alerta_foto', pi.alerta_foto, 'alerta_confirmado',pi.alerta_confirmado, 'alerta_em', pi.alerta_em ) ORDER BY pi.id ASC) FROM pedido_itens pi WHERE pi.pedido_id = p.id ) AS itens FROM cotacoes c LEFT JOIN pedidos p ON p.cotacao_id = c.id AND p.tenant_id = c.tenant_id WHERE c.user_id = $1 AND c.tenant_id = $2 ORDER BY c.id DESC, p.id DESC LIMIT 50`, [userId, tenantId] ) } static listAdmin(tenantId, { status, limit = 50, offset = 0 } = {}) { const params = [tenantId] let where = 'WHERE c.tenant_id = $1' if (status) { params.push(status); where += ` AND c.status = $${params.length}` } params.push(Math.min(Number(limit) || 50, 200)) params.push(Math.max(Number(offset) || 0, 0)) return query( `SELECT c.*, COALESCE(cl.nome, u.nome) AS cliente_nome, COALESCE(cl.tipo, 'PF') AS cliente_tipo FROM cotacoes c LEFT JOIN clientes cl ON cl.id = c.cliente_id LEFT JOIN users u ON u.id = c.user_id ${where} ORDER BY c.created_at DESC LIMIT $${params.length - 1} OFFSET $${params.length}`, params ) } static create({ tenant_id, cliente_id, user_id, status = 'rascunho', forma_pagto, total, desconto, observacao }) { return execute( `INSERT INTO cotacoes (tenant_id,cliente_id,user_id,status,forma_pagto,total,desconto,observacao) VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING *`, [tenant_id, cliente_id || null, user_id || null, status, forma_pagto || null, total || 0, desconto || 0, observacao || null] ) } static update(id, { status, forma_pagto, total, desconto, observacao }) { return execute( `UPDATE cotacoes SET status=$1,forma_pagto=$2,total=$3,desconto=$4,observacao=$5,updated_at=NOW() WHERE id=$6`, [status, forma_pagto || null, total, desconto || 0, observacao || null, id] ) } // ── Itens ───────────────────────────────────────────────── static listItens(cotacaoId) { return query( `SELECT ci.*, p.imagem AS produto_imagem FROM cotacao_itens ci LEFT JOIN produtos p ON p.id = ci.produto_id WHERE ci.cotacao_id = $1 ORDER BY ci.id ASC`, [cotacaoId] ) } static addItem({ cotacao_id, produto_id, nome_produto, sku, quantidade, preco_unitario, preco_promocional, desconto_pct, observacao }) { const precoFinal = preco_promocional || preco_unitario const subtotal = precoFinal * quantidade * (1 - (desconto_pct || 0) / 100) return execute( `INSERT INTO cotacao_itens (cotacao_id,produto_id,nome_produto,sku,quantidade,preco_unitario,preco_promocional,desconto_pct,subtotal,observacao) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10) RETURNING *`, [cotacao_id, produto_id || null, nome_produto, sku || null, quantidade, preco_unitario, preco_promocional || null, desconto_pct || 0, subtotal, observacao || null] ) } static updateItemObs(itemId, observacao) { return execute( `UPDATE cotacao_itens SET observacao=$1 WHERE id=$2`, [observacao || null, itemId] ) } static removeItem(itemId) { return execute('DELETE FROM cotacao_itens WHERE id = $1', [itemId]) } static clearItens(cotacaoId) { return execute('DELETE FROM cotacao_itens WHERE cotacao_id = $1', [cotacaoId]) } static async recalcTotal(cotacaoId) { await execute( `UPDATE cotacoes SET total = ( SELECT COALESCE(SUM(subtotal),0) FROM cotacao_itens WHERE cotacao_id = $1 ), updated_at = NOW() WHERE id = $1`, [cotacaoId] ) } // ── Converter cotação em pedido ─────────────────────────── static async toOrder(cotacaoId, { endereco_id, tipo_entrega, observacao } = {}) { const cotacao = await Cotacao.findById(cotacaoId) if (!cotacao) throw new Error('Cotação não encontrada') const itens = await Cotacao.listItens(cotacaoId) // Strategy 2 — se veio do portal (user_id preenchido mas cliente_id nulo), // garante um registro em clientes para manter rastreabilidade no admin let clienteId = cotacao.cliente_id if (!clienteId && cotacao.user_id) { // verifica se já existe um clientes vinculado a este user_id const existing = await queryOne( `SELECT id FROM clientes WHERE user_id = $1 AND tenant_id = $2 LIMIT 1`, [cotacao.user_id, cotacao.tenant_id] ) if (existing) { clienteId = existing.id } else { // cria o registro usando dados do users const user = await queryOne(`SELECT * FROM users WHERE id = $1`, [cotacao.user_id]) if (user) { const novo = await execute( `INSERT INTO clientes (tenant_id, user_id, nome, email, cpf_cnpj, tipo, created_at, updated_at) VALUES ($1,$2,$3,$4,$5,'PF',NOW(),NOW()) RETURNING id`, [cotacao.tenant_id, user.id, user.nome, user.email || null, user.cpf || null] ) clienteId = novo.rows[0]?.id || null } } } const pedidoResult = await execute( `INSERT INTO pedidos (tenant_id,cotacao_id,cliente_id,user_id,endereco_id,status,forma_pagto,tipo_entrega,total,desconto,observacao) VALUES ($1,$2,$3,$4,$5,'aguardando',$6,$7,$8,$9,$10) RETURNING *`, [cotacao.tenant_id, cotacaoId, clienteId, cotacao.user_id, endereco_id || null, cotacao.forma_pagto, tipo_entrega || 'retirada', cotacao.total, cotacao.desconto, observacao || cotacao.observacao] ) const pedido = pedidoResult.rows[0] if (!pedido) throw new Error('Falha ao criar pedido') for (const item of itens) { await execute( `INSERT INTO pedido_itens (pedido_id,produto_id,nome_produto,sku,quantidade,preco_unitario,desconto_pct,subtotal,observacao) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)`, [pedido.id, item.produto_id, item.nome_produto, item.sku, item.quantidade, item.preco_unitario, item.desconto_pct, item.subtotal, item.observacao || null] ) } // WPP-10 — gera protocolo único por pedido const dt = new Date() const yyyymmdd = `${dt.getFullYear()}${String(dt.getMonth()+1).padStart(2,'0')}${String(dt.getDate()).padStart(2,'0')}` const protocolo = `PED-${yyyymmdd}-${pedido.id}` await execute(`UPDATE pedidos SET protocolo=$1 WHERE id=$2`, [protocolo, pedido.id]) await execute( `UPDATE cotacoes SET status='finalizada', updated_at=NOW() WHERE id=$1`, [cotacaoId] ) return { ...pedido, protocolo } } } module.exports = Cotacao