Wave Reconciliation-Reviewed

Adding Ability to see reviewed transactions on the reconciliation page

// ==UserScript==
// @name         Wave Reconciliation-Reviewed
// @description  Adding Ability to see reviewed transactions on the reconciliation page
// @author       Seth Parrish
// @namespace    https://sethp.cc
// @version      0.5
// @icon         https://raw.githubusercontent.com/xthexder/wide-github/master/icon.png
// @homepageURL  https://greasyfork.org/en/scripts/394287-wave-reconciliation-reviewed
// @supportURL   https://greasyfork.org/en/scripts/394287-wave-reconciliation-reviewed
// @match        https://next.waveapps.com/*
// @grant        GM_registerMenuCommand
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_addStyle
// @license      GPL-3.0-only
// ==/UserScript==
"use strict";

// Global Vars & Storage Object
// var d = document
// var b = d.body
var DBNAME = "reviewed_db";
var db = GM_getValue(DBNAME) || [];
console.log("DB:", db);

// Add GM DB deletion option
GM_registerMenuCommand("Clear Stored Data", () => {
  var shouldClear = confirm(
    'Do you want to clear the "seen" reviewed transactions? (this will not effect data in wave)'
  );
  if (shouldClear) {
    db = [];
    GM_setValue(DBNAME, db);
    GM_deleteValue(DBNAME);
    console.log("DB:", db);
  }
});

// Add 'unmatch all' button
GM_registerMenuCommand("Unmatch All Transactions on screen", () => {
  var shouldGo = confirm(
    "Do you want to unmatch all the transactions currently on the page?"
  );
  if (shouldGo) {
    var className = "account-recv2__reconciliation__table__matched-icon--true";
    var icons = Array.from(document.getElementsByClassName(className));
    icons.forEach(function (x) {
      x.click();
    });
    console.log("Finished Unmatching Transactions");
  }
});

// Find transaction from reconciliaton
var lastPicked = "";

function findTransaction() {
  if (lastPicked != "") {
    var url = window.location.href.split("account-")[0];
    window.open(url + "transactions/" + lastPicked);
  } else {
    alert("You haven't picked a transaction yet");
  }
  console.log("Looking for transaction");
}

GM_registerMenuCommand("Find Transaction", () => {
  findTransaction();
});

// Adding Custom CSS
GM_addStyle(
  ".account-recv2__reconciliation__table__reviewed--true {background: #dec9ff !important;color: #849194 !important;}"
);

// Check for Reviewed Status and add to the DB
function processLatestTransactions(res) {
  var isReviewed = (x) => Boolean(x.transaction_status.value);
  var isInDB = (x) => db.some((e) => e.transaction_guid === x.transaction_guid);
  res.latest_transactions.forEach((x) => {
    if (isReviewed(x) && !isInDB(x)) {
      db.push(x);
    }
  });
  GM_setValue(DBNAME, db);
  console.log("DB: ", db);
}

// Handle changing the status of a single transaction
function processTransactionStatusChange(res) {
  // Do not run if we are marking as reviewed (other case handles)
  if (res.transaction_status.value) {
    return;
  }
  for (var i = 0; i < db.length; i++) {
    if (db[i].transaction_guid === res.guid) {
      db.splice(i, 1);
    }
  }
  GM_setValue("reviewed_db", db);
  console.log("DB: ", db);
}

// Utility function to apply custom css to a row with matching data
function turnGreen(date, desc, price_formatted) {
  var datedRows = Array.apply(
    null,
    document.querySelectorAll(`time[datetime="${date}"]`)
  ).map(function (x) {
    return x.parentNode.parentElement.parentElement.parentElement.parentElement;
  });
  if (datedRows.length === 0) {
    return;
  }
  var el = datedRows.find(function (x) {
    var txt = x.textContent; // Eg. "Nov 29, 2019Steam Games$13.90$1,568.22"
    return (
      txt.includes(date.split("-")[0]) &&
      txt.includes(desc) &&
      txt.includes(price_formatted)
    );
  });
  el.className += " account-recv2__reconciliation__table__reviewed--true ";
}

// Iterate DB and apply styles
function processReconciliationPage(res) {
  // Deal with this page's transaction data
  // var matched = res.transactions.filter(x => Boolean(x.is_matched))
  db.forEach(function (x) {
    turnGreen(
      x.transaction_date,
      x.transaction_description,
      x.transaction_total.transaction.formatted_string
    );
  });
}

// HTTP Listener
(function (open) {
  XMLHttpRequest.prototype.open = function () {
    this.addEventListener(
      "readystatechange",
      function () {
        var json = this.responseText ? JSON.parse(this.responseText) : {};
        var doneAndIncludes = (x) =>
          this.readyState === 4 && this.responseURL.includes(x);

        if (doneAndIncludes("/transactions/latest/")) {
          // Check for a finished request dealing with latest transactions
          processLatestTransactions(json);
        } else if (doneAndIncludes("/transactions/status/")) {
          // Check for a finished request, marking a transaction as "un-reviewed"
          processTransactionStatusChange(json);
        } else if (
          doneAndIncludes("/account_reconciliation/reconciliation_by_period/")
        ) {
          // Check for a finished request dealing with reconciliation transactions
          processReconciliationPage(json);
        } else if (
          doneAndIncludes("transactions/") &&
          this.responseURL.split("/").length == 8
        ) {
          // Grab last (selected) transaction and save to variable
          lastPicked = this.responseURL.split("/")[6];
          console.log("Last Picked Updated: ", lastPicked);
        }
      },
      false
    );
    open.apply(this, arguments);
  };
})(XMLHttpRequest.prototype.open);

// Keyboard Listener
function keyPress(e) {
  // this would test for whichever key is alt and the ctrl key at the same time
  if (e.ctrlKey && e.keyCode == 18) {
    findTransaction();
  }
}

// register the handler
document.addEventListener("keydown", keyPress);

// The End :)