Fix client identity when phone is added
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 58s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 58s
This commit is contained in:
@@ -14,7 +14,37 @@ 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 NORMALIZED_CUSTOMER_NAME_SQL = "NULLIF(LOWER(TRIM(regexp_replace(COALESCE(cliente_nome, ''), '\\s+', ' ', 'g'))), '')";
|
||||
// Phone-less historical rows must follow the later known phone for the same client name.
|
||||
const CUSTOMER_IDENTITY_CTE = `
|
||||
WITH order_identity AS (
|
||||
SELECT
|
||||
orders.*,
|
||||
${NORMALIZED_CUSTOMER_NAME_SQL} as normalized_customer_name
|
||||
FROM orders
|
||||
),
|
||||
customer_phone_by_name AS (
|
||||
SELECT
|
||||
normalized_customer_name,
|
||||
(ARRAY_AGG(NULLIF(cliente_fone, '') ORDER BY data_pedido_date DESC NULLS LAST, id DESC)
|
||||
FILTER (WHERE NULLIF(cliente_fone, '') IS NOT NULL))[1] as canonical_phone
|
||||
FROM order_identity
|
||||
WHERE normalized_customer_name IS NOT NULL
|
||||
GROUP BY normalized_customer_name
|
||||
),
|
||||
identity_orders AS (
|
||||
SELECT
|
||||
order_identity.*,
|
||||
COALESCE(
|
||||
NULLIF(order_identity.cliente_fone, ''),
|
||||
customer_phone_by_name.canonical_phone,
|
||||
'name:' || COALESCE(NULLIF(order_identity.cliente_nome, ''), 'Cliente Desconhecido')
|
||||
) as customer_key
|
||||
FROM order_identity
|
||||
LEFT JOIN customer_phone_by_name USING (normalized_customer_name)
|
||||
)
|
||||
`;
|
||||
const CUSTOMER_KEY_SQL = 'customer_key';
|
||||
|
||||
const getClientTokenSecret = () => (
|
||||
process.env.CLIENT_TOKEN_SECRET ||
|
||||
@@ -102,6 +132,28 @@ const resolveClientToken = async (clientToken) => {
|
||||
return result.rows[0]?.customer_key || null;
|
||||
};
|
||||
|
||||
const resolveCanonicalCustomerKey = async (customerKey) => {
|
||||
const normalizedCustomerKey = String(customerKey || '');
|
||||
if (!normalizedCustomerKey.startsWith('name:')) return customerKey;
|
||||
|
||||
const legacyName = normalizedCustomerKey.slice(5).trim();
|
||||
if (!legacyName) return customerKey;
|
||||
|
||||
const result = await pool.query(`
|
||||
${CUSTOMER_IDENTITY_CTE}
|
||||
SELECT customer_key
|
||||
FROM identity_orders
|
||||
WHERE normalized_customer_name = NULLIF(LOWER(TRIM(regexp_replace($1, '\\s+', ' ', 'g'))), '')
|
||||
ORDER BY
|
||||
NULLIF(cliente_fone, '') IS NOT NULL DESC,
|
||||
data_pedido_date DESC NULLS LAST,
|
||||
id DESC
|
||||
LIMIT 1;
|
||||
`, [legacyName]);
|
||||
|
||||
return result.rows[0]?.customer_key || customerKey;
|
||||
};
|
||||
|
||||
const normalizeDateParam = (value) => {
|
||||
if (!value) return null;
|
||||
|
||||
@@ -605,6 +657,7 @@ const getProductDetailsAnalytics = async (productId, range = {}) => {
|
||||
const getClientAnalytics = async (range = {}) => {
|
||||
const { params, whereClause } = buildDateFilter(range);
|
||||
const result = await pool.query(`
|
||||
${CUSTOMER_IDENTITY_CTE}
|
||||
SELECT
|
||||
${CUSTOMER_KEY_SQL} as customer_key,
|
||||
MAX(COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido')) as name,
|
||||
@@ -613,7 +666,7 @@ const getClientAnalytics = async (range = {}) => {
|
||||
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(data_pedido_date) as last_purchase_date
|
||||
FROM orders
|
||||
FROM identity_orders
|
||||
${whereClause}
|
||||
GROUP BY customer_key
|
||||
ORDER BY total_spent DESC;
|
||||
@@ -699,10 +752,11 @@ const getOrderGroupKey = (row) => (
|
||||
const getClientDetailsAnalytics = async (clientToken, range = {}) => {
|
||||
const customerKey = await resolveClientToken(clientToken);
|
||||
if (!customerKey) return null;
|
||||
const resolvedCustomerKey = await resolveCanonicalCustomerKey(customerKey);
|
||||
|
||||
const normalizedStart = normalizeDateParam(range.start);
|
||||
const normalizedEnd = normalizeDateParam(range.end);
|
||||
const periodParams = [customerKey];
|
||||
const periodParams = [resolvedCustomerKey];
|
||||
const periodFilters = [
|
||||
`${CUSTOMER_KEY_SQL} = $1`,
|
||||
'data_pedido_date IS NOT NULL'
|
||||
@@ -720,15 +774,17 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
|
||||
|
||||
const [summaryResult, periodResult] = await Promise.all([
|
||||
pool.query(`
|
||||
${CUSTOMER_IDENTITY_CTE}
|
||||
SELECT
|
||||
MAX(COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido')) as name,
|
||||
MAX(NULLIF(cliente_fone, '')) as phone,
|
||||
COUNT(DISTINCT COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text))::int as all_time_order_count
|
||||
FROM orders
|
||||
FROM identity_orders
|
||||
WHERE ${CUSTOMER_KEY_SQL} = $1
|
||||
AND data_pedido_date IS NOT NULL;
|
||||
`, [customerKey]),
|
||||
`, [resolvedCustomerKey]),
|
||||
pool.query(`
|
||||
${CUSTOMER_IDENTITY_CTE}
|
||||
SELECT
|
||||
cliente_nome,
|
||||
cliente_fone,
|
||||
@@ -746,7 +802,7 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
|
||||
marketplace,
|
||||
canal_venda,
|
||||
numero_ecommerce
|
||||
FROM orders
|
||||
FROM identity_orders
|
||||
WHERE ${periodFilters.join(' AND ')}
|
||||
ORDER BY data_pedido_date DESC, COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text) DESC;
|
||||
`, periodParams)
|
||||
@@ -877,6 +933,7 @@ const getRfmAnalytics = async (range = {}) => {
|
||||
await client.query(`SET LOCAL statement_timeout = '${RFM_QUERY_TIMEOUT_MS}ms'`);
|
||||
|
||||
const periodResult = await client.query(`
|
||||
${CUSTOMER_IDENTITY_CTE}
|
||||
SELECT
|
||||
${CUSTOMER_KEY_SQL} as customer_key,
|
||||
MAX(COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido')) as name,
|
||||
@@ -886,7 +943,7 @@ const getRfmAnalytics = async (range = {}) => {
|
||||
COALESCE(SUM(quantidade), 0) as quantity_purchased,
|
||||
MAX(data_pedido_date) as last_purchase_date,
|
||||
GREATEST((${periodRecencyReferenceDate} - MAX(data_pedido_date))::int, 0) as recency_days
|
||||
FROM orders
|
||||
FROM identity_orders
|
||||
${whereClause}
|
||||
GROUP BY customer_key
|
||||
ORDER BY monetary DESC;
|
||||
@@ -926,7 +983,8 @@ const getRfmAnalytics = async (range = {}) => {
|
||||
const customerKeysParam = `$${historyParams.push(periodCustomerKeys)}::text[]`;
|
||||
|
||||
const historyResult = await client.query(`
|
||||
WITH customer_history AS (
|
||||
${CUSTOMER_IDENTITY_CTE},
|
||||
customer_history AS (
|
||||
SELECT
|
||||
${CUSTOMER_KEY_SQL} as customer_key,
|
||||
MAX(COALESCE(NULLIF(cliente_nome, ''), 'Cliente Desconhecido')) as name,
|
||||
@@ -936,7 +994,7 @@ const getRfmAnalytics = async (range = {}) => {
|
||||
COALESCE(SUM(quantidade), 0) as quantity_purchased,
|
||||
MAX(data_pedido_date) as last_purchase_date,
|
||||
GREATEST((${recencyReferenceDate} - MAX(data_pedido_date))::int, 0) as recency_days
|
||||
FROM orders
|
||||
FROM identity_orders
|
||||
WHERE ${historyFilters.join(' AND ')}
|
||||
GROUP BY customer_key
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user