I built an iOS widget to display marketplace sales totals

Who doesn’t love checking their sales numbers a million times per day :sweat_smile:

I thought it would be nicer as a widget in my local currency…

Here’s how to set it up:

  1. Use at your own risk! (obviously)
  2. Install this free app: https://scriptable.app
  3. In the marketplace dashboard create a new user with view sales reports permissions.
  4. Create an API key for that user; ensuring it is the correct user.
  5. Open the Scriptable app, paste this code and edit to suit.
  6. Add the Scriptable widget to your iPhone homescreen and connect the script.
const EMAIL = "";
const API_KEY = "";
const VENDOR_ID = "";
const CURRENCY = "AUD";
const BACKGROUND = "#202123";

// get exchange rate
const rate = await get({
  url: `https://api.exchangerate.host/latest?symbols=${CURRENCY}&base=USD`,
});

// get sales data
let sales = {},
  totalMonth = 0,
  totalAll = 0;

sales = await fetchSalesMonth();
sales.total.series.forEach((hosting) =>
  hosting.elements.forEach((sale) => (totalMonth += sale.revenue))
);
totalMonth = totalMonth * rate.rates[CURRENCY];

sales = await fetchSalesAll();
sales.total.series.forEach((hosting) =>
  hosting.elements.forEach((sale) => (totalAll += sale.revenue))
);
totalAll = totalAll * rate.rates[CURRENCY];

// create widget
const widget = new ListWidget();
widget.backgroundColor = new Color(BACKGROUND);
const stack = widget.addStack();
stack.layoutVertically();
stack.centerAlignContent();

const titleMonth = stack.addText("SALES");
titleMonth.textColor = Color.gray();
titleMonth.font = new Font("Helvetica", 12);

const textMonth = stack.addText(
  "$" + totalMonth.toLocaleString(undefined, { maximumFractionDigits: 0 })
);
textMonth.textColor = Color.white();
textMonth.font = new Font("Helvetica", 28);

stack.addSpacer(10);

const titleAll = stack.addText("ALL TIME");
titleAll.textColor = Color.gray();
titleAll.font = new Font("Helvetica", 12);

const textAll = stack.addText(
  "$" + totalAll.toLocaleString(undefined, { maximumFractionDigits: 0 })
);
textAll.textColor = Color.white();
textAll.font = new Font("Helvetica", 28);

Script.setWidget(widget);
Script.complete();
widget.presentSmall();

// functions
async function fetchSalesMonth() {
  const now = new Date();
  const start = formatDate(new Date(now.getFullYear(), now.getMonth(), 1));
  const end = formatDate(now);
  return await get({
    url: `https://marketplace.atlassian.com/rest/2/vendors/${VENDOR_ID}/reporting/sales/transactions/hosting?startDate=${start}&endDate=${end}`,
    headers: { Authorization: `Basic ${btoa(`${EMAIL}:${API_KEY}`)}` },
  });
}

async function fetchSalesAll() {
  return await get({
    url: `https://marketplace.atlassian.com/rest/2/vendors/${VENDOR_ID}/reporting/sales/transactions/hosting?aggregation=month`,
    headers: { Authorization: `Basic ${btoa(`${EMAIL}:${API_KEY}`)}` },
  });
}

async function get(opts) {
  const request = new Request(opts.url);
  request.headers = {
    ...opts.headers,
    ...this.defaultHeaders,
  };
  var result = await request.loadJSON();

  return result;
}

function formatDate(date) {
  var d = new Date(date),
    month = "" + (d.getMonth() + 1),
    day = "" + d.getDate(),
    year = d.getFullYear();

  if (month.length < 2) month = "0" + month;
  if (day.length < 2) day = "0" + day;

  return [year, month, day].join("-");
}

4 Likes