Optimize products list analytics loading
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 54s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 54s
This commit is contained in:
@@ -401,19 +401,42 @@ const getDashboardAnalytics = async (range = {}) => {
|
||||
const getProductAnalytics = async (range = {}) => {
|
||||
const { params, whereClause } = buildDateFilter(range);
|
||||
const result = await pool.query(`
|
||||
WITH period_sales AS (
|
||||
SELECT
|
||||
produto_id as id,
|
||||
MAX(COALESCE(NULLIF(produto_descricao, ''), 'Unknown')) as order_name,
|
||||
COALESCE(SUM(quantidade), 0) as quantity_sold,
|
||||
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue,
|
||||
COUNT(*)::int as order_line_count,
|
||||
MIN(data_pedido_date) as first_sale_date,
|
||||
MAX(data_pedido_date) as last_sale_date,
|
||||
(ARRAY_AGG(valor_unitario ORDER BY data_pedido_date DESC NULLS LAST, data_pedido DESC NULLS LAST))[1] as last_price
|
||||
FROM orders
|
||||
${whereClause}
|
||||
GROUP BY produto_id
|
||||
),
|
||||
stock_rows AS (
|
||||
SELECT
|
||||
produto_id as id,
|
||||
MAX(NULLIF(nome, '')) as stock_name,
|
||||
COALESCE(MAX(saldo), 0) as stock
|
||||
FROM stock
|
||||
GROUP BY produto_id
|
||||
)
|
||||
SELECT
|
||||
COALESCE(${PRODUCT_NAME_SQL}, 'Unknown') as name,
|
||||
MAX(produto_id) as id,
|
||||
COALESCE(SUM(quantidade), 0) as quantity_sold,
|
||||
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue,
|
||||
COUNT(*)::int as order_line_count,
|
||||
MIN(data_pedido_date) as first_sale_date,
|
||||
MAX(data_pedido_date) as last_sale_date
|
||||
FROM orders
|
||||
${whereClause}
|
||||
GROUP BY name
|
||||
ORDER BY revenue DESC, quantity_sold DESC
|
||||
LIMIT 500;
|
||||
COALESCE(period_sales.id, stock_rows.id) as id,
|
||||
COALESCE(stock_rows.stock_name, period_sales.order_name, 'Unknown') as name,
|
||||
COALESCE(period_sales.quantity_sold, 0) as quantity_sold,
|
||||
COALESCE(period_sales.revenue, 0) as revenue,
|
||||
COALESCE(period_sales.order_line_count, 0)::int as order_line_count,
|
||||
period_sales.first_sale_date,
|
||||
period_sales.last_sale_date,
|
||||
COALESCE(period_sales.last_price, 0) as last_price,
|
||||
COALESCE(stock_rows.stock, 0) as stock
|
||||
FROM period_sales
|
||||
FULL OUTER JOIN stock_rows ON stock_rows.id = period_sales.id
|
||||
WHERE COALESCE(period_sales.id, stock_rows.id) IS NOT NULL
|
||||
ORDER BY quantity_sold DESC, revenue DESC, name ASC;
|
||||
`, params);
|
||||
|
||||
return result.rows.map(row => ({
|
||||
@@ -422,6 +445,8 @@ const getProductAnalytics = async (range = {}) => {
|
||||
quantitySold: toNumber(row.quantity_sold),
|
||||
revenue: toNumber(row.revenue),
|
||||
orderLineCount: toNumber(row.order_line_count),
|
||||
lastPrice: toNumber(row.last_price),
|
||||
stock: toNumber(row.stock),
|
||||
firstSaleDate: row.first_sale_date,
|
||||
lastSaleDate: row.last_sale_date
|
||||
}));
|
||||
|
||||
@@ -11,6 +11,7 @@ const {
|
||||
getClientAnalytics,
|
||||
getClientDetailsAnalytics,
|
||||
getPreviousDate,
|
||||
getProductAnalytics,
|
||||
getRecencyScore,
|
||||
getRfmAnalytics,
|
||||
getRfmSegment,
|
||||
@@ -330,6 +331,67 @@ test('buildRfmClients applies lifecycle protections to new, hibernating, at-risk
|
||||
assert.equal(byKey.get('lost').rfmScore, '113');
|
||||
});
|
||||
|
||||
test('getProductAnalytics returns exact product rows with stock and latest price', async () => {
|
||||
const originalQuery = pool.query;
|
||||
const calls = [];
|
||||
|
||||
pool.query = async (sql, params = []) => {
|
||||
calls.push({ sql, params });
|
||||
|
||||
return {
|
||||
rows: [
|
||||
{
|
||||
id: '919483307',
|
||||
name: 'BASE LISA CAMISETA COR PRETO TAMANHO - G',
|
||||
quantity_sold: 9513,
|
||||
revenue: 113216.83,
|
||||
order_line_count: 100,
|
||||
last_price: 11.9,
|
||||
stock: 11731,
|
||||
first_sale_date: '2026-06-01',
|
||||
last_sale_date: '2026-06-22'
|
||||
},
|
||||
{
|
||||
id: 'stock-only',
|
||||
name: 'Produto sem venda no período',
|
||||
quantity_sold: 0,
|
||||
revenue: 0,
|
||||
order_line_count: 0,
|
||||
last_price: 0,
|
||||
stock: 12,
|
||||
first_sale_date: null,
|
||||
last_sale_date: null
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
const products = await getProductAnalytics({ start: '2026-06-01', end: '2026-06-22' });
|
||||
|
||||
assert.equal(calls.length, 1);
|
||||
assert.match(calls[0].sql, /WITH period_sales AS/);
|
||||
assert.match(calls[0].sql, /FULL OUTER JOIN stock_rows/);
|
||||
assert.doesNotMatch(calls[0].sql, /GROUP BY name/);
|
||||
assert.deepEqual(calls[0].params, ['2026-06-01', '2026-06-22']);
|
||||
assert.deepEqual(products[0], {
|
||||
id: '919483307',
|
||||
name: 'BASE LISA CAMISETA COR PRETO TAMANHO - G',
|
||||
quantitySold: 9513,
|
||||
revenue: 113216.83,
|
||||
orderLineCount: 100,
|
||||
lastPrice: 11.9,
|
||||
stock: 11731,
|
||||
firstSaleDate: '2026-06-01',
|
||||
lastSaleDate: '2026-06-22'
|
||||
});
|
||||
assert.equal(products[1].id, 'stock-only');
|
||||
assert.equal(products[1].stock, 12);
|
||||
} finally {
|
||||
pool.query = originalQuery;
|
||||
}
|
||||
});
|
||||
|
||||
test('getClientAnalytics returns opaque client tokens', async () => {
|
||||
const originalQuery = pool.query;
|
||||
const calls = [];
|
||||
|
||||
Reference in New Issue
Block a user