Rollback analytics to ed1f129b07
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m5s

This commit is contained in:
Cauê Faleiros
2026-06-17 12:13:48 -03:00
parent aedfedca76
commit 10c69457ba
8 changed files with 78 additions and 239 deletions

View File

@@ -1,5 +1,4 @@
const { pool } = require('../db');
const { ORDER_DATE_SQL } = require('../sql/orderDateSql');
const SIZE_SUFFIX_SQL_PATTERN = '\\s+-\\s+(?:(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2})(?:/(?:PP|P|M|G|GG|XG|XGG|EG|EGG|EXG|U|UNICO|ÚNICO|\\d{2}))*)$';
const PRODUCT_NAME_SQL = `
@@ -8,7 +7,6 @@ const PRODUCT_NAME_SQL = `
ELSE NULLIF(TRIM(regexp_replace(split_part(COALESCE(produto_descricao, 'Unknown'), ' TAMANHO', 1), '${SIZE_SUFFIX_SQL_PATTERN}', '', 'i')), '')
END
`;
const CUSTOMER_KEY_SQL = "COALESCE(NULLIF(cliente_fone, ''), 'name:' || COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido'))";
const normalizeDateParam = (value) => {
if (!value) return null;
@@ -35,18 +33,18 @@ const normalizeDateParam = (value) => {
const buildDateFilter = ({ start, end } = {}) => {
const params = [];
const filters = [`${ORDER_DATE_SQL} IS NOT NULL`];
const filters = ['data_pedido_date IS NOT NULL'];
const normalizedStart = normalizeDateParam(start);
const normalizedEnd = normalizeDateParam(end);
if (normalizedStart) {
params.push(normalizedStart);
filters.push(`${ORDER_DATE_SQL} >= $${params.length}::date`);
filters.push(`data_pedido_date >= $${params.length}::date`);
}
if (normalizedEnd) {
params.push(normalizedEnd);
filters.push(`${ORDER_DATE_SQL} <= $${params.length}::date`);
filters.push(`data_pedido_date <= $${params.length}::date`);
}
return {
@@ -213,8 +211,8 @@ const getProductAnalytics = async (range = {}) => {
COALESCE(SUM(quantidade), 0) as quantity_sold,
COALESCE(SUM(quantidade * valor_unitario), 0) as revenue,
COUNT(*)::int as order_line_count,
MIN(${ORDER_DATE_SQL}) as first_sale_date,
MAX(${ORDER_DATE_SQL}) as last_sale_date
MIN(data_pedido_date) as first_sale_date,
MAX(data_pedido_date) as last_sale_date
FROM orders
${whereClause}
GROUP BY name
@@ -237,21 +235,19 @@ const getClientAnalytics = async (range = {}) => {
const { params, whereClause } = buildDateFilter(range);
const result = await pool.query(`
SELECT
${CUSTOMER_KEY_SQL} as customer_key,
MAX(COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido')) as name,
COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido') as name,
MAX(NULLIF(cliente_fone, '')) as phone,
COALESCE(SUM(quantidade), 0) as quantity_purchased,
COALESCE(SUM(quantidade * valor_unitario), 0) as total_spent,
COUNT(DISTINCT COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text))::int as order_count,
MAX(${ORDER_DATE_SQL}) as last_purchase_date
MAX(data_pedido_date) as last_purchase_date
FROM orders
${whereClause}
GROUP BY customer_key
GROUP BY COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido')
ORDER BY total_spent DESC;
`, params);
return result.rows.map(row => ({
customerKey: row.customer_key,
name: row.name,
phone: row.phone || '',
quantityPurchased: toNumber(row.quantity_purchased),
@@ -265,69 +261,62 @@ const getRfmAnalytics = async (range = {}) => {
const { params, whereClause } = buildDateFilter(range);
const normalizedStart = normalizeDateParam(range.start);
const normalizedEnd = normalizeDateParam(range.end);
const periodRecencyReferenceDate = normalizedEnd ? `$${params.length}::date` : 'CURRENT_DATE';
const recencyReferenceDate = normalizedEnd ? '$1::date' : 'CURRENT_DATE';
const historyParams = normalizedEnd ? [normalizedEnd] : [];
const usePeriodAsHistory = !normalizedStart || normalizedStart <= '2000-01-01';
const tagReference = getPreviousDate(normalizedStart) || normalizedEnd;
const recencyReferenceDate = tagReference ? '$1::date' : 'CURRENT_DATE';
const historyParams = tagReference ? [tagReference] : [];
const periodQuery = pool.query(`
const [periodResult, historyResult] = await Promise.all([
pool.query(`
SELECT
${CUSTOMER_KEY_SQL} as customer_key,
MAX(cliente_nome) as name,
MAX(NULLIF(cliente_fone, '')) as phone,
cliente_fone as phone,
COALESCE(SUM(quantidade * valor_unitario), 0) as monetary,
COUNT(DISTINCT COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text))::int as frequency,
COALESCE(SUM(quantidade), 0) as quantity_purchased,
MAX(${ORDER_DATE_SQL}) as last_purchase_date,
GREATEST((${periodRecencyReferenceDate} - MAX(${ORDER_DATE_SQL}))::int, 0) as recency_days
MAX(data_pedido_date) as last_purchase_date
FROM orders
${whereClause}
GROUP BY customer_key
AND cliente_fone IS NOT NULL
AND cliente_fone != ''
GROUP BY cliente_fone
ORDER BY monetary DESC;
`, params);
const historyQuery = usePeriodAsHistory ? null : pool.query(`
`, params),
pool.query(`
SELECT
${CUSTOMER_KEY_SQL} as customer_key,
MAX(cliente_nome) as name,
MAX(NULLIF(cliente_fone, '')) as phone,
cliente_fone as phone,
COALESCE(SUM(quantidade * valor_unitario), 0) as monetary,
COUNT(DISTINCT COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text))::int as frequency,
COALESCE(SUM(quantidade), 0) as quantity_purchased,
MAX(${ORDER_DATE_SQL}) as last_purchase_date,
GREATEST((${recencyReferenceDate} - MAX(${ORDER_DATE_SQL}))::int, 0) as recency_days
MAX(data_pedido_date) as last_purchase_date,
GREATEST((${recencyReferenceDate} - MAX(data_pedido_date))::int, 0) as recency_days
FROM orders
WHERE ${ORDER_DATE_SQL} IS NOT NULL
AND ${ORDER_DATE_SQL} <= ${recencyReferenceDate}
GROUP BY customer_key;
`, historyParams);
WHERE data_pedido_date IS NOT NULL
AND data_pedido_date <= ${recencyReferenceDate}
AND cliente_fone IS NOT NULL
AND cliente_fone != ''
GROUP BY cliente_fone;
`, historyParams)
]);
const [periodResult, historyResult] = historyQuery
? await Promise.all([periodQuery, historyQuery])
: [await periodQuery, null];
const historyRows = historyResult ? historyResult.rows : periodResult.rows;
const historyClients = buildRfmClients(historyRows.map(row => ({
customerKey: row.customer_key,
const historyClients = buildRfmClients(historyResult.rows.map(row => ({
name: row.name,
phone: row.phone || '',
phone: row.phone,
monetary: toNumber(row.monetary),
frequency: toNumber(row.frequency),
quantityPurchased: toNumber(row.quantity_purchased),
lastPurchaseDate: row.last_purchase_date,
recencyDays: toNumber(row.recency_days)
})));
const tagsByCustomerKey = new Map(historyClients.map(client => [client.customerKey, client]));
const tagsByPhone = new Map(historyClients.map(client => [client.phone, client]));
const clients = periodResult.rows.map(row => {
const taggedClient = tagsByCustomerKey.get(row.customer_key);
const taggedClient = tagsByPhone.get(row.phone);
if (!taggedClient) {
const newCustomerSegment = getRfmSegment(3, 1);
return {
customerKey: row.customer_key,
name: row.name,
phone: row.phone || '',
phone: row.phone,
monetary: toNumber(row.monetary),
frequency: toNumber(row.frequency),
quantityPurchased: toNumber(row.quantity_purchased),
@@ -345,9 +334,8 @@ const getRfmAnalytics = async (range = {}) => {
return {
...taggedClient,
customerKey: row.customer_key,
name: row.name,
phone: row.phone || '',
phone: row.phone,
monetary: toNumber(row.monetary),
frequency: toNumber(row.frequency),
quantityPurchased: toNumber(row.quantity_purchased),
@@ -384,6 +372,5 @@ module.exports = {
getDashboardAnalytics,
getProductAnalytics,
normalizeDateParam,
ORDER_DATE_SQL,
scoreTertile
};