Windowsfreak / Deutsche Bank Vermögensübersicht

// ==UserScript==
// @name           Deutsche Bank Vermögensübersicht
// @name:de        Deutsche Bank Vermögensübersicht
// @namespace      https://windowsfreak.de
// @version        4.0
// @description    Collects historical Vermögensübersicht data for Vermögensentwicklung.
// @description:de Sammelt historische Daten aus der Vermögensübersicht für die Vermögensentwicklung.
// @author         Björn Eberhardt
// @license        MIT; https://opensource.org/licenses/MIT
// @icon           https://www.deutsche-bank.de/dam/deutschebank/de/shared/logo/deutsche_bank_logo_retina.gif
// @match          https://meine.deutsche-bank.de/trxm/db/invoke/*show.assets.overview.do
// @match          https://meine.deutsche-bank.de/trxm/db/invoke/*show.assets.overview.do?tab=purchase
// @grant          none
// ==/UserScript==
// MIT license used to import to OpenUserJS


(function () {
    'use strict';

    const $ = id => document.getElementById(id)
    const $tt = name => document.getElementsByTagName(name)
    const $cc = name => document.getElementsByClassName(name)
    const $qsa = name => document.querySelectorAll(name)

    // Navigated to Vermögensaufstellung?
    if ($tt('h1')[0].innerText !== 'Vermögensaufstellung') {
        // Highlight menu item to find it faster
        const link = [...$tt('a')].filter(item => item.text === 'Vermögensaufstellung')[0]
        link.style.fontWeight = 'bold'
        link.style.textDecoration = 'underline'
    } else {
        const navigate = true // set to false to stop scraping through history
        const user = $('customerNumber').childNodes[1].data.replace(/^\s+|\s+$/g, '').replace(/\s/g, '_')
        const reportDateFields = ['reportDateYear', 'reportDateMonth', 'reportDateDay']
        const parseLocaleNumber = stringNumber => {
            const thousandSeparator = (1111).toLocaleString().replace(/1/g, '')
            const decimalSeparator = (1.1).toLocaleString().replace(/1/g, '')

            return parseFloat(
                stringNumber
                    .replace(new RegExp('\\' + thousandSeparator, 'g'), '')
                    .replace(new RegExp('\\' + decimalSeparator), '.')
            )
        }
        const navigateTo = target => {
            const url = new URL(window.location.href)
            for (let param of url.searchParams.keys()) {
                if (param.indexOf('show') >= 0) {
                    url.searchParams.delete(param)
                }
            }
            url.searchParams.append(target, '1')
            window.location.href = url.href
        }
        const done = () => {
            console.log('done')
            const d = document.createElement('div')
            d.innerHTML = 'Der Abruf der Daten wurde erfolgreich beendet.<br /> <input type="button" value="Tabelle zeigen" class="button nextStep"> <input type="button" value="Diagramm zeigen" class="button nextStep">'
            d.getElementsByTagName('input')[0].onclick = () => navigateTo('showTable')
            d.getElementsByTagName('input')[1].onclick = () => navigateTo('showChart')
            $('assetsOverviewForm').prepend(d)
        }
        const to8Char = parts => `${parts[0]}${parts[1]}${parts[2]}`
        const parseDate = (date, str = date.toISOString()) => [str.substring(0, 4), str.substring(5, 7), str.substring(8, 10)]
        const formatDate = d => d.toLocaleDateString('de-DE', {
            timeZone: 'UTC',
            day: '2-digit',
            month: '2-digit',
            year: 'numeric'
        })
        const getDate = () => reportDateFields.map(key => $(key).value)
        const setDate = date => reportDateFields.forEach((key, index) => $(key).value = date[index]) || $cc('button refresh')[0].click()
        const getCat = elem => {
            while (elem) {
                const cat = elem.querySelector('#subTotal01')
                if (cat) {
                    return cat.innerText
                }
                elem = elem.previousElementSibling
            }
        }
        const parseNum = node => parseLocaleNumber(node.innerText)

        // Do we see data?
        if ($cc('errorMsg').length < 1) {
            // Gather it.
            const d = getDate()
            const today = new Date(Date.UTC(d[0], d[1] - 1, d[2], 0, 0, 0, 0))
            const todayStr = to8Char(d)

            // Do we have data from yesterday?
            const yesterday = new Date(today)
            yesterday.setDate(yesterday.getDate() - 1)
            const yesterdayStr = to8Char(parseDate(yesterday))

            const promisify = request =>
                new Promise((resolve, reject) => {
                    request.onsuccess = event => resolve(event.target.result)
                    request.onerror = event => reject(event.target.error)
                })

            const getDb = () => {
                return new Promise((resolve, reject) => {
                    const db = indexedDB.open('wf', 1)
                    db.onupgradeneeded = function(event) {
                        const db = event.target.result
                        // Create object stores and indexes
                        const daily = db.createObjectStore('daily', { keyPath: ['user', 'd_str'] })
                        db.createObjectStore('lists')
                        daily.createIndex('user_dstr', ['user', 'd_str'])
                    }
                    db.onsuccess = event => resolve(event.target.result)
                    db.onerror = event => {
                        console.error(event)
                        reject(new Error('Error opening database'))
                    }
                })
            }
            const getData = db => {
                return new Promise((resolve, reject) => {
                    const transaction = db.transaction(['daily', 'lists'], 'readonly')
                    const lists = transaction.objectStore('lists')
                    const daily = transaction.objectStore('daily')

                    const resultObj = {
                        db
                    }
                    promisify(lists.get(`${user}_wkn`)).then(wkn => {
                        if (!wkn) wkn = {}
                        resultObj.wkn = wkn
                        return promisify(lists.get(`${user}_cat`))
                    }).then(cat => {
                        if (!cat) cat = []
                        resultObj.cat = cat
                        return promisify(daily.get([user, yesterdayStr]))
                    }).then(yesterday => {
                        resultObj.yesterday = yesterday
                        resolve(resultObj)
                    }).catch(error => {
                        console.error(error)
                        reject(new Error('Error retrieving database entries'))
                    })
                })
            }
            const setData = data => {
                return new Promise((resolve, reject) => {
                    const transaction = data.db.transaction(['daily', 'lists'], 'readwrite')
                    const lists = transaction.objectStore('lists')
                    const daily = transaction.objectStore('daily')
                    promisify(lists.put(data.wkn, `${user}_wkn`)).then(() => {
                        return promisify(lists.put(data.cat, `${user}_cat`))
                    }).then(() => {
                        return promisify(daily.put(data.day))
                    }).then(() => {
                        resolve()
                    }).catch(error => {
                        console.error(error)
                        reject(new Error('Error storing database entries'))
                    })
                })
            }

            getDb().then(getData).then(data => {
                const wkns = {}
                const cats = {}

                let bbal = 0, bchg = 0
                $qsa('#lkn_detail').forEach(e => {
                    const wkn = e.title.split(' ')[1]
                    data.wkn[wkn] = e.innerText
                    const cat = getCat(e.closest('tr'))
                    const bal = parseNum(e.closest('tr').querySelector('td[headers=\'col08a\']'))
                    const chg = parseNum(e.closest('tr').querySelector('td[headers=\'col07a\']'))
                    wkns[wkn] = {cat, wkn, bal, chg}
                })
                $qsa('#subTotal01').forEach(e => {
                    const cat = e.innerText
                    data.cat.includes(cat) || data.cat.push(cat)
                    const bal = parseNum(e.closest('tr').querySelector('th[headers=\'subTotal01 col08a\']'))
                    const chg = parseNum(e.closest('tr').querySelector('th[headers=\'subTotal01 col07a\']'))
                    if (cat === 'Liquidität') {
                        bbal = bal
                        bchg = chg
                    }
                    cats[cat] = {cat, bal, chg}
                })
                const tbal = parseNum(document.querySelector('td[headers=\'textTotal col08a\']'))
                const tchg = parseNum(document.querySelector('td[headers=\'textTotal col07a\']'))
                data.day = {user, d_str: todayStr, d: formatDate(today), tbal, tchg, bbal, bchg, wkns, cats}

                setData(data).then(() => {
                    if (navigate && !data.yesterday) {
                        setDate(parseDate(yesterday))
                    } else {
                        done()
                    }
                })
            })
        } else {
            done()
        }
    }
})()