Account Sync Details

These values are already generated for this seller account.

Sheet URLhttps://docs.google.com/spreadsheets/d/1MX6Z5EzveUB9h7YdKZy5_3M69YUi8WDLA3OzDX0Xekw/edit?usp=sharing
Sync endpointhttps://dashmetric.ecomrevup.com/api/sheet-sync/cmo7qr22f0000xyf0e2lzrlg6
Last sync6/6/2026, 8:43:47 AM
Open Accounts Page

One-Time Setup

After this, syncing can happen automatically without manual exports.

1. Open your Google Sheet

Use the private sheet linked to this seller account.

2. Open Extensions → Apps Script

Create a script project attached directly to that sheet.

3. Paste the script below

Save it, then run the sync once so Google can ask for permission.

4. Add a trigger

Set it to sync on edit or every 15 to 30 minutes.

Apps Script

Copy this into the Google Sheet’s Apps Script editor for Pasignia AU.

const SYNC_URL = 'https://dashmetric.ecomrevup.com/api/sheet-sync/cmo7qr22f0000xyf0e2lzrlg6';
const WEBHOOK_SECRET = 'cmo7qr22g0001xyf0ltss8jt6';

function syncDashboardData() {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheets = spreadsheet.getSheets();
  const tabs = sheets.map((sheet) => sheet.getName());
  const rows = [];
  const metricMap = {};

  sheets.forEach((sheet) => {
    const values = sheet.getDataRange().getDisplayValues();
    if (values.length < 2) return;

    const headers = values[0].map((header) => normalizeKey_(header));
    for (let rowIndex = 1; rowIndex < values.length; rowIndex += 1) {
      const cells = values[rowIndex];
      if (!cells.some((cell) => String(cell).trim() !== '')) continue;

      const record = {
        id: sheet.getName() + '-' + (rowIndex + 1),
        sourceTab: sheet.getName(),
        date: null,
        year: null,
        monthNumber: null,
        weekNumber: null,
        dayOfMonth: null,
        sku: null,
        asin: null,
        metrics: {}
      };

      headers.forEach((header, index) => {
        const value = cells[index];
        if (value === '' || value == null) return;

        if (matches_(header, ['date', 'report_date', 'calendar_date'])) {
          record.date = normalizeDate_(value);
          return;
        }
        if (matches_(header, ['year', 'year_number'])) {
          record.year = toNumber_(value);
          return;
        }
        if (matches_(header, ['month', 'month_number', 'month_no'])) {
          record.monthNumber = toNumber_(value);
          return;
        }
        if (matches_(header, ['week', 'week_number', 'week_no'])) {
          record.weekNumber = toNumber_(value);
          return;
        }
        if (matches_(header, ['day', 'day_of_month', 'day_number'])) {
          record.dayOfMonth = toNumber_(value);
          return;
        }
        if (matches_(header, ['sku', 'seller_sku', 'child_sku'])) {
          record.sku = String(value);
          return;
        }
        if (matches_(header, ['asin', 'child_asin', 'parent_asin'])) {
          record.asin = String(value);
          return;
        }

        const numeric = parseMetric_(value);
        if (numeric == null) return;
        record.metrics[header] = numeric;
        if (!metricMap[header]) {
          metricMap[header] = inferMetric_(header);
        }
      });

      if (Object.keys(record.metrics).length > 0) {
        rows.push(record);
      }
    }
  });

  const payload = {
    spreadsheetId: spreadsheet.getId(),
    spreadsheetTitle: spreadsheet.getName(),
    tabs: tabs,
    metricCatalog: Object.keys(metricMap).map((key) => ({
      key: key,
      label: metricMap[key].label,
      format: metricMap[key].format,
      aggregation: metricMap[key].aggregation
    })),
    rows: rows
  };

  const response = UrlFetchApp.fetch(SYNC_URL, {
    method: 'post',
    contentType: 'application/json',
    headers: {
      'x-webhook-secret': WEBHOOK_SECRET
    },
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  Logger.log(response.getResponseCode());
  Logger.log(response.getContentText());
}

function normalizeKey_(value) {
  return String(value || '')
    .trim()
    .toLowerCase()
    .replace(/[\s/()-]+/g, '_')
    .replace(/_+/g, '_')
    .replace(/^_|_$/g, '');
}

function matches_(value, aliases) {
  return aliases.indexOf(value) !== -1;
}

function toNumber_(value) {
  const parsed = Number(String(value).replace(/[^0-9.-]/g, ''));
  return Number.isFinite(parsed) ? parsed : null;
}

function normalizeDate_(value) {
  if (Object.prototype.toString.call(value) === '[object Date]' && !isNaN(value.getTime())) {
    return Utilities.formatDate(value, 'UTC', 'yyyy-MM-dd');
  }
  const text = String(value).trim();
  if (!text) return null;
  return text;
}

function parseMetric_(value) {
  const text = String(value).trim();
  if (!text) return null;
  const isPercent = text.indexOf('%') !== -1;
  const parsed = Number(text.replace(/[$,₹€£%\s]/g, ''));
  if (!Number.isFinite(parsed)) return null;
  return isPercent ? parsed / 100 : parsed;
}

function inferMetric_(key) {
  if (/(acos|ctr|cvr|rate|share|margin|percent|percentage|tacos|roas_)/i.test(key)) {
    return { label: titleize_(key), format: 'percent', aggregation: 'average' };
  }
  if (/(roas|aov|cpc|cpm|rpc|value|multiplier)/i.test(key)) {
    return { label: titleize_(key), format: 'decimal', aggregation: 'average' };
  }
  if (/(revenue|sales|spend|cost|fees|profit|amount|budget)/i.test(key)) {
    return { label: titleize_(key), format: 'currency', aggregation: 'sum' };
  }
  if (/(orders|units|clicks|impressions|sessions|page_views|purchases)/i.test(key)) {
    return { label: titleize_(key), format: 'integer', aggregation: 'sum' };
  }
  return { label: titleize_(key), format: 'decimal', aggregation: 'sum' };
}

function titleize_(value) {
  return value
    .split('_')
    .filter(Boolean)
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(' ');
}

Latest Snapshot

The dashboard builder will use the most recent sync received from Apps Script.

SpreadsheetPasignia - Dashboard
Rows stored4809
Synced at6/6/2026, 8:43:47 AM
Dashboards1