@@ -967,6 +975,19 @@ function renderProfile(p) {
document.querySelectorAll('.checkout-section').forEach(function(el) {
el.classList.toggle('visible', isMerchant);
});
+ // Relabel hero cards for merchants to clarify CambioPay-only
+ var heroLabels = document.querySelectorAll('#heroGrid .hero-label');
+ heroLabels.forEach(function(lbl) {
+ if (isMerchant) {
+ if (lbl.textContent === 'Volume USD') lbl.textContent = 'CambioPay Volume';
+ else if (lbl.textContent === 'Transacoes') lbl.textContent = 'CambioPay Ops';
+ else if (lbl.textContent === 'Receita USD') lbl.textContent = 'Receita CambioPay';
+ } else {
+ if (lbl.textContent === 'CambioPay Volume') lbl.textContent = 'Volume USD';
+ else if (lbl.textContent === 'CambioPay Ops') lbl.textContent = 'Transacoes';
+ else if (lbl.textContent === 'Receita CambioPay') lbl.textContent = 'Receita USD';
+ }
+ });
}
function renderChurnRisk(churn) {
diff --git a/src/queries/bi.queries.js b/src/queries/bi.queries.js
index 87ddd69..e157223 100644
--- a/src/queries/bi.queries.js
+++ b/src/queries/bi.queries.js
@@ -15,29 +15,40 @@ async function fetchBIData(dataInicio, dataFim, getAgenteName = null) {
[trendBrlUsd], [trendUsdBrl], [trendUsdUsd],
[topClients], [retention], [clientsAtRisk], [agentRanking]
] = await Promise.all([
- // 1. BRL→USD KPIs
+ // 1. BRL→USD KPIs (real revenue = P&L formula)
pool.execute(`
SELECT COUNT(*) as qtd,
- ROUND(COALESCE(SUM(amount_usd), 0), 2) as vol_usd,
- ROUND(COALESCE(SUM(amount_brl), 0), 2) as vol_brl,
- ROUND(COALESCE(SUM((exchange_rate - ptax) / exchange_rate * amount_usd), 0), 2) as spread_revenue,
- ROUND(COALESCE(AVG((exchange_rate - ptax) / exchange_rate * 100), 0), 2) as avg_spread_pct,
- COUNT(DISTINCT id_conta) as clientes
- FROM br_transaction_to_usa
- WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
+ ROUND(COALESCE(SUM(t.amount_usd), 0), 2) as vol_usd,
+ ROUND(COALESCE(SUM(t.amount_brl), 0), 2) as vol_brl,
+ ROUND(COALESCE(SUM(
+ (ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2) - COALESCE(t.pfee, 0))
+ - (t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0))
+ ), 0), 2) as spread_revenue,
+ ROUND(COALESCE(AVG((t.exchange_rate - t.ptax) / t.exchange_rate * 100), 0), 2) as avg_spread_pct,
+ COUNT(DISTINCT t.id_conta) as clientes
+ FROM br_transaction_to_usa t
+ JOIN br_payment_methods pm ON t.payment_method_id = pm.id
+ WHERE DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
+ AND t.ptax IS NOT NULL AND t.ptax > 0
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
`, [dataInicio, dataFim]),
- // 2. USD→BRL KPIs
+ // 2. USD→BRL KPIs (real revenue: spread + fee for non-balance, fee only for balance)
pool.execute(`
SELECT COUNT(*) as qtd,
- ROUND(COALESCE(SUM(valor), 0), 2) as vol_usd,
- ROUND(COALESCE(SUM(valor_sol), 0), 2) as vol_brl,
- ROUND(COALESCE(SUM((ptax - cotacao) / ptax * valor), 0), 2) as spread_revenue,
- ROUND(COALESCE(AVG(CASE WHEN cotacao > 0 THEN (ptax - cotacao) / ptax * 100 ELSE 0 END), 0), 2) as avg_spread_pct,
- COUNT(DISTINCT id_conta) as clientes
- FROM pagamento_br
- WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
- AND cotacao IS NOT NULL AND cotacao > 0
- AND (pgto IS NULL OR pgto != 'balance')
+ ROUND(COALESCE(SUM(pb.valor), 0), 2) as vol_usd,
+ ROUND(COALESCE(SUM(pb.valor_sol), 0), 2) as vol_brl,
+ ROUND(COALESCE(SUM(
+ CASE WHEN pb.tipo_envio = 'balance' THEN COALESCE(pb.fee, 0)
+ ELSE COALESCE(CASE WHEN pb.ptax IS NOT NULL AND pb.ptax > 0 THEN ((pb.ptax - pb.cotacao) * pb.valor) / pb.ptax ELSE 0 END, 0) + COALESCE(pb.fee, 0)
+ END
+ ), 0), 2) as spread_revenue,
+ ROUND(COALESCE(AVG(CASE WHEN pb.cotacao > 0 AND pb.tipo_envio != 'balance' THEN (pb.ptax - pb.cotacao) / pb.ptax * 100 ELSE 0 END), 0), 2) as avg_spread_pct,
+ COUNT(DISTINCT pb.id_conta) as clientes
+ FROM pagamento_br pb
+ WHERE pb.valor > 0 AND pb.data_cp IS NOT NULL AND pb.data_cp <> '0000-00-00'
+ AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) >= ?
+ AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) <= ?
`, [dataInicio, dataFim]),
// 3. USD→USD KPIs
pool.execute(`
@@ -56,17 +67,31 @@ async function fetchBIData(dataInicio, dataFim, getAgenteName = null) {
SELECT id_conta FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
) all_clients
`, [dataInicio, dataFim, dataInicio, dataFim]),
- // 5. Previous period
+ // 5. Previous period (real revenue formulas)
pool.execute(`
- SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(amount_usd),0),2) as vol_usd,
- ROUND(COALESCE(SUM((exchange_rate - ptax) / exchange_rate * amount_usd),0),2) as spread_revenue
- FROM br_transaction_to_usa WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
+ SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(t.amount_usd),0),2) as vol_usd,
+ ROUND(COALESCE(SUM(
+ (ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2) - COALESCE(t.pfee, 0))
+ - (t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0))
+ ),0),2) as spread_revenue
+ FROM br_transaction_to_usa t
+ JOIN br_payment_methods pm ON t.payment_method_id = pm.id
+ WHERE DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
+ AND t.ptax IS NOT NULL AND t.ptax > 0
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
`, [prevStartStr, prevEndStr]),
pool.execute(`
- SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(valor),0),2) as vol_usd,
- ROUND(COALESCE(SUM((ptax - cotacao) / ptax * valor),0),2) as spread_revenue
- FROM pagamento_br WHERE DATE(created_at) >= ? AND DATE(created_at) <= ?
- AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
+ SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(pb.valor),0),2) as vol_usd,
+ ROUND(COALESCE(SUM(
+ CASE WHEN pb.tipo_envio = 'balance' THEN COALESCE(pb.fee, 0)
+ ELSE COALESCE(CASE WHEN pb.ptax IS NOT NULL AND pb.ptax > 0 THEN ((pb.ptax - pb.cotacao) * pb.valor) / pb.ptax ELSE 0 END, 0) + COALESCE(pb.fee, 0)
+ END
+ ),0),2) as spread_revenue
+ FROM pagamento_br pb
+ WHERE pb.valor > 0 AND pb.data_cp IS NOT NULL AND pb.data_cp <> '0000-00-00'
+ AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) >= ?
+ AND DATE(CASE WHEN pb.tipo_envio = 'balance' THEN pb.data_cp ELSE pb.created_at END) <= ?
`, [prevStartStr, prevEndStr]),
pool.execute(`
SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(valor),0),2) as vol_usd
@@ -133,22 +158,35 @@ async function fetchBIData(dataInicio, dataFim, getAgenteName = null) {
FROM pagamento_br p INNER JOIN conta c ON c.id_conta = p.id_conta GROUP BY c.nome
) combined GROUP BY nome HAVING MAX(last_op) < CURDATE() ORDER BY total_usd DESC LIMIT 20
`),
- // 12. Agent ranking
+ // 12. Agent ranking (real revenue formulas)
pool.execute(`
SELECT agente_id, SUM(vol) as total_usd, SUM(qtd) as total_qtd,
ROUND(SUM(spread_rev), 2) as total_spread, COUNT(DISTINCT client_id) as clientes
FROM (
SELECT ac.agente_id, t.id_conta as client_id, SUM(t.amount_usd) as vol, COUNT(*) as qtd,
- SUM((t.exchange_rate - t.ptax) / t.exchange_rate * t.amount_usd) as spread_rev
- FROM br_transaction_to_usa t INNER JOIN ag_contas ac ON ac.conta_id = t.id_conta
+ SUM(
+ (ROUND((t.amount_brl - IF(pm.provider IN ('ouribank','bs2'), 0, t.fee)) / t.ptax, 2) - COALESCE(t.pfee, 0))
+ - (t.amount_usd + COALESCE(t.bonus_valor, 0) - COALESCE(t.taxa_cr, 0))
+ ) as spread_rev
+ FROM br_transaction_to_usa t
+ INNER JOIN ag_contas ac ON ac.conta_id = t.id_conta
+ INNER JOIN br_payment_methods pm ON t.payment_method_id = pm.id
WHERE DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
+ AND t.ptax IS NOT NULL AND t.ptax > 0
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
GROUP BY ac.agente_id, t.id_conta
UNION ALL
SELECT ac.agente_id, p.id_conta as client_id, SUM(p.valor) as vol, COUNT(*) as qtd,
- SUM((p.ptax - p.cotacao) / p.ptax * p.valor) as spread_rev
+ SUM(
+ CASE WHEN p.tipo_envio = 'balance' THEN COALESCE(p.fee, 0)
+ ELSE COALESCE(CASE WHEN p.ptax IS NOT NULL AND p.ptax > 0 THEN ((p.ptax - p.cotacao) * p.valor) / p.ptax ELSE 0 END, 0) + COALESCE(p.fee, 0)
+ END
+ ) as spread_rev
FROM pagamento_br p INNER JOIN ag_contas ac ON ac.conta_id = p.id_conta
- WHERE DATE(p.created_at) >= ? AND DATE(p.created_at) <= ?
- AND p.cotacao IS NOT NULL AND p.cotacao > 0 AND (p.pgto IS NULL OR p.pgto != 'balance')
+ WHERE DATE(CASE WHEN p.tipo_envio = 'balance' THEN p.data_cp ELSE p.created_at END) >= ?
+ AND DATE(CASE WHEN p.tipo_envio = 'balance' THEN p.data_cp ELSE p.created_at END) <= ?
+ AND p.valor > 0 AND p.data_cp IS NOT NULL AND p.data_cp <> '0000-00-00'
GROUP BY ac.agente_id, p.id_conta
) combined GROUP BY agente_id ORDER BY total_usd DESC LIMIT 10
`, [dataInicio, dataFim, dataInicio, dataFim])
diff --git a/src/queries/client.queries.js b/src/queries/client.queries.js
index 1d545a9..21f38cc 100644
--- a/src/queries/client.queries.js
+++ b/src/queries/client.queries.js
@@ -21,7 +21,9 @@ async function fetchTopClients() {
COUNT(*) AS cnt,
COUNT(DISTINCT DATE_FORMAT(created_at, '%Y-%m')) AS months_active,
MAX(created_at) AS last_op
- FROM br_transaction_to_usa GROUP BY id_conta
+ FROM br_transaction_to_usa
+ WHERE (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
+ GROUP BY id_conta
) t1 ON t1.id_conta = c.id_conta
LEFT JOIN (
SELECT id_conta,
@@ -92,6 +94,7 @@ async function fetchClientProfile(clienteId) {
ROUND(COALESCE(SUM((exchange_rate - ptax) / exchange_rate * amount_usd),0),2) as spread_revenue,
MIN(created_at) as first_op, MAX(created_at) as last_op
FROM br_transaction_to_usa WHERE id_conta = ?
+ AND (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
`, [clienteId]);
const [usd] = await conn.execute(`
@@ -122,6 +125,7 @@ async function fetchClientProfile(clienteId) {
const [monthsRows] = await conn.execute(`
SELECT COUNT(DISTINCT mes) as months_active FROM (
SELECT DATE_FORMAT(created_at, '%Y-%m') as mes FROM br_transaction_to_usa WHERE id_conta = ?
+ AND (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
UNION
SELECT DATE_FORMAT(created_at, '%Y-%m') as mes FROM pagamento_br WHERE id_conta = ?
AND cotacao IS NOT NULL AND cotacao > 0 AND (pgto IS NULL OR pgto != 'balance')
@@ -165,6 +169,7 @@ async function fetchClientData(clienteId, dataInicio, dataFim) {
ROUND(COALESCE(SUM((exchange_rate - ptax) / exchange_rate * amount_usd),0),2) as spread_revenue,
ROUND(COALESCE(AVG((exchange_rate - ptax) / exchange_rate * 100),0),2) as avg_spread_pct
FROM br_transaction_to_usa WHERE id_conta = ? AND DATE(created_at) >= ? AND DATE(created_at) <= ?
+ AND (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
`, [clienteId, dataInicio, dataFim]);
// KPIs USD->BRL
@@ -182,6 +187,7 @@ async function fetchClientData(clienteId, dataInicio, dataFim) {
SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(amount_usd),0),2) as vol_usd,
ROUND(COALESCE(SUM((exchange_rate - ptax) / exchange_rate * amount_usd),0),2) as spread_revenue
FROM br_transaction_to_usa WHERE id_conta = ? AND DATE(created_at) >= ? AND DATE(created_at) <= ?
+ AND (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
`, [clienteId, prevStartStr, prevEndStr]);
const [prevUsd] = await conn.execute(`
SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(valor),0),2) as vol_usd,
@@ -196,6 +202,7 @@ async function fetchClientData(clienteId, dataInicio, dataFim) {
ROUND(SUM(amount_usd),2) as vol_usd,
ROUND(AVG((exchange_rate - ptax) / exchange_rate * 100),2) as avg_spread
FROM br_transaction_to_usa WHERE id_conta = ? AND DATE(created_at) >= ? AND DATE(created_at) <= ?
+ AND (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
GROUP BY DATE(created_at) ORDER BY dia
`, [clienteId, dataInicio, dataFim]);
@@ -220,6 +227,7 @@ async function fetchClientData(clienteId, dataInicio, dataFim) {
FROM br_transaction_to_usa t
LEFT JOIN br_payment_methods pm ON t.payment_method_id = pm.id
WHERE t.id_conta = ? AND DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
ORDER BY t.created_at DESC
`, [clienteId, dataInicio, dataFim]);
@@ -241,6 +249,7 @@ async function fetchClientData(clienteId, dataInicio, dataFim) {
const [dowBrl] = await conn.execute(`
SELECT DAYOFWEEK(created_at) as dow, COUNT(*) as qtd, ROUND(SUM(amount_usd),2) as vol_usd
FROM br_transaction_to_usa WHERE id_conta = ? AND DATE(created_at) >= ? AND DATE(created_at) <= ?
+ AND (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
GROUP BY DAYOFWEEK(created_at)
`, [clienteId, dataInicio, dataFim]);
const [dowUsd] = await conn.execute(`
@@ -256,6 +265,7 @@ async function fetchClientData(clienteId, dataInicio, dataFim) {
FROM br_transaction_to_usa t
LEFT JOIN br_payment_methods pm ON t.payment_method_id = pm.id
WHERE t.id_conta = ? AND DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
GROUP BY pm.provider
`, [clienteId, dataInicio, dataFim]);
const [provUsd] = await conn.execute(`
@@ -272,6 +282,7 @@ async function fetchClientData(clienteId, dataInicio, dataFim) {
ROUND(SUM(amount_usd),2) as vol_usd,
ROUND(SUM((exchange_rate - ptax) / exchange_rate * amount_usd),2) as spread_revenue
FROM br_transaction_to_usa WHERE id_conta = ? AND DATE(created_at) >= ? AND DATE(created_at) <= ?
+ AND (status IN ('boleto_pago','finalizado') OR date_sent_usa <> '0000-00-00 00:00:00')
GROUP BY DATE_FORMAT(created_at, '%Y-%m') ORDER BY mes
`, [clienteId, dataInicio, dataFim]);
const [monthlyUsd] = await conn.execute(`
@@ -378,6 +389,7 @@ async function fetchMerchantProfile(clienteId) {
FROM br_cb_cobranca cb
INNER JOIN br_transaction_to_usa t ON t.cobranca_id = cb.id
WHERE cb.empresa_id = ?
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
`, [empresaId]);
const [rev] = await conn.execute(`
@@ -433,6 +445,7 @@ async function fetchMerchantData(empresaId, dataInicio, dataFim) {
FROM br_cb_cobranca cb
INNER JOIN br_transaction_to_usa t ON t.cobranca_id = cb.id
WHERE cb.empresa_id = ? AND DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
`, [empresaId, dataInicio, dataFim]);
const [rev] = await conn.execute(`
@@ -458,6 +471,7 @@ async function fetchMerchantData(empresaId, dataInicio, dataFim) {
FROM br_cb_cobranca cb
INNER JOIN br_transaction_to_usa t ON t.cobranca_id = cb.id
WHERE cb.empresa_id = ? AND DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
`, [empresaId, prevStartStr, prevEndStr]);
const [prevRev] = await conn.execute(`
@@ -486,6 +500,7 @@ async function fetchMerchantData(empresaId, dataInicio, dataFim) {
FROM br_cb_cobranca cb
INNER JOIN br_transaction_to_usa t ON t.cobranca_id = cb.id
WHERE cb.empresa_id = ? AND DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
GROUP BY DATE_FORMAT(t.created_at, '%Y-%m') ORDER BY mes
`, [empresaId, dataInicio, dataFim]);
@@ -495,6 +510,7 @@ async function fetchMerchantData(empresaId, dataInicio, dataFim) {
INNER JOIN br_transaction_to_usa t ON t.cobranca_id = cb.id
INNER JOIN conta c ON c.id_conta = t.id_conta
WHERE cb.empresa_id = ? AND DATE(t.created_at) >= ? AND DATE(t.created_at) <= ?
+ AND (t.status IN ('boleto_pago','finalizado') OR t.date_sent_usa <> '0000-00-00 00:00:00')
GROUP BY t.id_conta, c.nome
ORDER BY vol_usd DESC LIMIT 10
`, [empresaId, dataInicio, dataFim]);