ga4-export-fixer 0.9.0-dev.10 → 0.9.0-dev.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -235,13 +235,15 @@ const itemListAttributionExpr = (lookbackType, timestampColumn, lookbackTimeMs)
235
235
  frameBounds = `range between ${lookbackMicros} preceding and current row`;
236
236
  }
237
237
 
238
- return `last_value(
238
+ // Refund events occur outside the selection-driven journey window — propagating
239
+ // item_list_* attribution into them would misattribute. Suppress attribution on refund rows.
240
+ return `if(event_name = 'refund', null, last_value(
239
241
  if(${selectEvents}, ${structExpr}, null) ignore nulls
240
242
  ) over(
241
243
  partition by ${partitionBy}
242
244
  order by ${timestampColumn} asc
243
245
  ${frameBounds}
244
- )`;
246
+ ))`;
245
247
  };
246
248
 
247
249
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ga4-export-fixer",
3
- "version": "0.9.0-dev.10",
3
+ "version": "0.9.0-dev.12",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -162,8 +162,18 @@ const _generateEnhancedEventsSQL = (mergedConfig) => {
162
162
 
163
163
  // item list attribution config
164
164
  const itemListAttribution = mergedConfig.itemListAttribution;
165
- const ecommerceEventsFilter = itemListAttribution
166
- ? helpers.ga4EcommerceEvents.filter(e => e !== 'refund').map(e => `'${e}'`).join(', ')
165
+
166
+ // Build enrichment-source CTEs and gather per-level join/column data. The utility routes
167
+ // event-level and item-level entries through separate output channels. Done up here so the
168
+ // items-scaffold activation state is known before building event_data (which needs
169
+ // _item_row_id when the scaffold is active for any reason).
170
+ const { steps: enrichmentSteps, event: eventEnrichments, item: itemEnrichments }
171
+ = utils.buildEnrichments(mergedConfig.enrichments);
172
+ const itemEnrichmentsActive = itemEnrichments.joins.length > 0;
173
+ const itemsScaffoldActive = !!itemListAttribution || itemEnrichmentsActive;
174
+
175
+ const ecommerceEventsFilter = itemsScaffoldActive
176
+ ? helpers.ga4EcommerceEvents.map(e => `'${e}'`).join(', ')
167
177
  : null;
168
178
 
169
179
  // auto-adjust bufferDays for time-based item list attribution lookback
@@ -220,7 +230,7 @@ const _generateEnhancedEventsSQL = (mergedConfig) => {
220
230
  // ecommerce
221
231
  ecommerce: helpers.fixEcommerceStruct('ecommerce'),
222
232
  // assign a unique row id, used for handling item-level attribution and enrichment
223
- _item_row_id: itemListAttribution ? helpers.itemRowId(ecommerceEventsFilter) : undefined,
233
+ _item_row_id: itemsScaffoldActive ? helpers.itemRowId(ecommerceEventsFilter) : undefined,
224
234
  // flag if the data is "final" and is not expected to change anymore
225
235
  data_is_final: helpers.isFinalData(mergedConfig.dataIsFinal.detectionMethod, mergedConfig.dataIsFinal.dayThreshold),
226
236
  export_type: helpers.getGa4ExportType('_table_suffix'),
@@ -263,11 +273,6 @@ ${excludedEventsSQL}`,
263
273
  'group by': 'session_id',
264
274
  };
265
275
 
266
- // Build enrichment-source CTEs and gather per-level join/column data. The utility routes
267
- // event-level and item-level entries through separate output channels.
268
- const { steps: enrichmentSteps, event: eventEnrichments, item: itemEnrichments }
269
- = utils.buildEnrichments(mergedConfig.enrichments);
270
-
271
276
  // Validate item-level joinKey columns and collect any event_data columns that need to
272
277
  // be carried up to items_unnested as top-level columns (so the LEFT JOIN inside
273
278
  // items_rebuilt can USING(...) on them). Item-struct fields are already top-level on
@@ -300,8 +305,6 @@ ${excludedEventsSQL}`,
300
305
  // LEFT JOIN enrich_<name> for each item-level enrichment.
301
306
  // Activation: emitted when EITHER itemListAttribution is configured OR at least one
302
307
  // item-level enrichment is present.
303
- const itemEnrichmentsActive = itemEnrichments.joins.length > 0;
304
- const itemsScaffoldActive = !!itemListAttribution || itemEnrichmentsActive;
305
308
  const itemListSteps = itemsScaffoldActive ? (() => {
306
309
  const passthroughEvents = `event_name in ('view_item_list', 'select_item', 'view_promotion', 'select_promotion')`;
307
310
 
@@ -316,8 +319,8 @@ ${excludedEventsSQL}`,
316
319
 
317
320
  // Carry up any event_data joinKey columns used by item-level enrichments so the
318
321
  // USING(...) clause in items_rebuilt can bind against top-level identifiers.
319
- // Skip ones already in the base columns above (e.g. event_date is always carried).
320
- const baseColumnNames = new Set(['_item_row_id', 'event_name', 'event_date', ...Object.keys(itemFieldColumns)]);
322
+ // Skip ones already in the base columns above
323
+ const baseColumnNames = new Set(['_item_row_id', 'event_name', ...Object.keys(itemFieldColumns)]);
321
324
  const extraJoinKeyColumns = {};
322
325
  for (const c of itemJoinKeysFromEventData) {
323
326
  if (!baseColumnNames.has(c)) {
@@ -331,8 +334,6 @@ ${excludedEventsSQL}`,
331
334
  const unnestedSelectColumns = {
332
335
  '_item_row_id': '_item_row_id',
333
336
  'event_name': 'event_name',
334
- // event_date is carried forward for ability to use it in data enrichment joins
335
- 'event_date': 'event_date',
336
337
  ...itemFieldColumns,
337
338
  ...extraJoinKeyColumns,
338
339
  };