diff --git a/ajax/dashboard/globalProcess/availablePlugins.php b/ajax/dashboard/globalProcess/availablePlugins.php
new file mode 100644
index 0000000000000000000000000000000000000000..725f422b65714a96fad9de870e2b3bf81e9d3005
--- /dev/null
+++ b/ajax/dashboard/globalProcess/availablePlugins.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_erp_ajax_dashboard_globalProcess_availablePlugins
+ */
+
+use QUI\ERP\Processes;
+
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_dashboard_globalProcess_availablePlugins',
+    function () {
+        $PackageManager = QUI::getPackageManager();
+        $Processes = new Processes();
+        $plugins = [];
+
+        foreach ($Processes->getWantedPluginList() as $plugin) {
+            if ($PackageManager->isInstalled($plugin)) {
+                $plugins[] = $plugin;
+            }
+        }
+
+        return $plugins;
+    },
+    [],
+    ['Permission::checkAdminUser']
+);
diff --git a/ajax/dashboard/globalProcess/getList.php b/ajax/dashboard/globalProcess/getList.php
new file mode 100644
index 0000000000000000000000000000000000000000..410cc1f921a3eda85bfe7719e4bd8612517c3674
--- /dev/null
+++ b/ajax/dashboard/globalProcess/getList.php
@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_erp_ajax_dashboard_globalProcess_getList
+ */
+
+use QUI\ERP\Processes;
+
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_dashboard_globalProcess_getList',
+    function () {
+        return (new Processes())->getList();
+    },
+    [],
+    ['Permission::checkAdminUser']
+);
diff --git a/ajax/dashboard/globalProcess/getProcess.php b/ajax/dashboard/globalProcess/getProcess.php
new file mode 100644
index 0000000000000000000000000000000000000000..b366dcdff4ffa649329d7a36cc47b80523c56160
--- /dev/null
+++ b/ajax/dashboard/globalProcess/getProcess.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * This file contains package_quiqqer_erp_ajax_dashboard_globalProcess_getProcess
+ */
+
+use QUI\ERP\Process;
+
+QUI::$Ajax->registerFunction(
+    'package_quiqqer_erp_ajax_dashboard_globalProcess_getProcess',
+    function ($globalProcessId) {
+        $Process = new Process($globalProcessId);
+
+        return [
+            'history' => $Process->getCompleteHistory()->toArray()
+        ];
+    },
+    ['globalProcessId'],
+    ['Permission::checkAdminUser']
+);
diff --git a/bin/backend/controls/Comments.css b/bin/backend/controls/Comments.css
index 842f08f5c648234fb91d2c431a3bcbdc77304d27..2456669f86cb6a854f4bcb013321efc130af5e8e 100644
--- a/bin/backend/controls/Comments.css
+++ b/bin/backend/controls/Comments.css
@@ -11,6 +11,16 @@
     width: 100%;
 }
 
+.quiqqer-erp-comments-comment--clickable:hover {
+    background: #2F8FC6;
+    color: #FFFFFF;
+    cursor: pointer;
+}
+
+.quiqqer-erp-comments-comment--clickable:hover .quiqqer-erp-comments-comment-type {
+    color: #FFFFFF;
+}
+
 /** title
  ================================================== */
 
diff --git a/bin/backend/controls/Comments.html b/bin/backend/controls/Comments.html
index eac3944f613c0b15da07e97f29594ce8bdb0c08c..53026d4b6a1d4c1e3ff58f77418f23098911359c 100644
--- a/bin/backend/controls/Comments.html
+++ b/bin/backend/controls/Comments.html
@@ -21,6 +21,7 @@
     <div class="quiqqer-erp-comments-comment"
          data-id="{{id}}"
          data-source="{{source}}"
+         data-object-hash="{{objectHash}}"
     >
         <span class="quiqqer-erp-comments-comment-type" title="{{title}}"
               {{#editable}}data-editable="1"{{/editable}}
diff --git a/bin/backend/controls/Comments.js b/bin/backend/controls/Comments.js
index 6362a3777682b77e492579bdfb0e7e96fd667d9c..65f31bbb396f646f96eb1f4f78eb65f61eb8d530 100644
--- a/bin/backend/controls/Comments.js
+++ b/bin/backend/controls/Comments.js
@@ -10,31 +10,33 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
 
     'qui/QUI',
     'qui/controls/Control',
+    'utils/Panels',
     'Mustache',
     'Locale',
 
     'text!package/quiqqer/erp/bin/backend/controls/Comments.html',
     'css!package/quiqqer/erp/bin/backend/controls/Comments.css'
 
-], function (QUI, QUIControl, Mustache, QUILocale, template) {
-    "use strict";
+], function(QUI, QUIControl, PanelUtils, Mustache, QUILocale, template) {
+    'use strict';
 
     const lg = 'quiqqer/erp';
 
     return new Class({
 
         Extends: QUIControl,
-        Type   : 'package/quiqqer/erp/bin/backend/controls/Comments',
+        Type: 'package/quiqqer/erp/bin/backend/controls/Comments',
 
         Binds: [
-            '$onCreate'
+            '$onCreate',
+            '$onEntryClick'
         ],
 
         options: {
             comments: false
         },
 
-        initialize: function (options) {
+        initialize: function(options) {
             this.parent(options);
 
             this.$filter = false;
@@ -50,7 +52,7 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
          *
          * @returns {HTMLDivElement}
          */
-        create: function () {
+        create: function() {
             this.$Elm = this.parent();
 
             this.$Elm.addClass('quiqqer-erp-comments');
@@ -59,12 +61,20 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
             return this.$Elm;
         },
 
+        /**
+         * empties the comment list
+         */
+        clear: function() {
+            this.$comments = [];
+            this.$Elm.set('html', '');
+        },
+
         /**
          * insert / set comments
          *
          * @param {String|Object} comments
          */
-        unserialize: function (comments) {
+        unserialize: function(comments) {
             if (typeOf(comments) === 'string') {
                 try {
                     comments = JSON.decode(comments);
@@ -78,7 +88,7 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
 
             const Formatter = this.$getFormatter();
 
-            comments = comments.map(function (entry) {
+            comments = comments.map(function(entry) {
                 let date = new Date(entry.time * 1000),
                     type = 'fa fa-comment';
 
@@ -106,19 +116,24 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
                     entry.id = '';
                 }
 
+                if (typeof entry.objectHash === 'undefined') {
+                    entry.objectHash = '';
+                }
+
                 if (typeof entry.editable === 'undefined') {
                     entry.editable = false;
                 }
 
                 return {
-                    date     : date,
-                    time     : Formatter.format(date),
-                    message  : entry.message,
-                    type     : type,
+                    date: date,
+                    time: Formatter.format(date),
+                    message: entry.message,
+                    type: type,
                     timestamp: entry.time,
-                    id       : entry.id,
-                    source   : entry.source,
-                    editable : entry.editable
+                    id: entry.id,
+                    source: entry.source,
+                    editable: entry.editable,
+                    objectHash: entry.objectHash
                 };
             });
 
@@ -135,7 +150,7 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
 
                 if (typeof group[day] === 'undefined') {
                     group[day] = {
-                        day : day,
+                        day: day,
                         data: []
                     };
                 }
@@ -151,14 +166,15 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
                 }
 
                 group[day].data.push({
-                    time     : entry.time,
-                    message  : entry.message,
-                    type     : entry.type,
+                    time: entry.time,
+                    message: entry.message,
+                    type: entry.type,
                     timestamp: entry.timestamp,
-                    id       : entry.id,
-                    source   : entry.source,
-                    title    : entry.source !== '' ? title : '',
-                    editable : entry.editable
+                    id: entry.id,
+                    source: entry.source,
+                    title: entry.source !== '' ? title : '',
+                    editable: entry.editable,
+                    objectHash: entry.objectHash
                 });
             }
 
@@ -169,18 +185,18 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
         /**
          * refresh the display
          */
-        refresh: function () {
+        refresh: function() {
             let i, data, realData, commentEntries;
             const self = this;
             const comments = [];
 
-            const sortComments = function (a, b) {
+            const sortComments = function(a, b) {
                 return a.timestamp - b.timestamp;
             };
 
             const commentClone = Object.clone(this.$comments);
 
-            const filterComments = function (entry) {
+            const filterComments = function(entry) {
                 const message = entry.message.toLowerCase();
                 const type = entry.type.toLowerCase();
                 const id = entry.id.toLowerCase();
@@ -223,12 +239,25 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
 
             this.$Elm.set({
                 html: Mustache.render(template, {
-                    comments      : comments,
+                    comments: comments,
                     textNoComments: QUILocale.get(lg, 'comments.message.no.comments')
                 })
             });
 
-            this.$Elm.getElements('[data-editable]').addEvent('click', function (event) {
+            this.$Elm.querySelectorAll('.quiqqer-erp-comments-comment').forEach((Comment) => {
+                if (!Comment.get('data-object-hash')) {
+                    return;
+                }
+
+                if (typeof QUIQQER_FRONTEND !== 'undefined') {
+                    return;
+                }
+
+                Comment.addClass('quiqqer-erp-comments-comment--clickable');
+                Comment.addEventListener('click', this.$onEntryClick);
+            });
+
+            this.$Elm.getElements('[data-editable]').addEvent('click', function(event) {
                 let Parent = event.target;
 
                 if (!Parent.hasClass('quiqqer-erp-comments-comment')) {
@@ -236,8 +265,9 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
                 }
 
                 const data = {
-                    id    : Parent.get('data-id'),
-                    source: Parent.get('data-source')
+                    id: Parent.get('data-id'),
+                    source: Parent.get('data-source'),
+                    objectHash: Parent.get('data-object-hash')
                 };
 
                 self.fireEvent('edit', [
@@ -253,14 +283,14 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
          *
          * @return {window.Intl.DateTimeFormat}
          */
-        $getFormatter: function () {
+        $getFormatter: function() {
             let locale = QUILocale.getCurrent();
 
             const options = {
                 // year  : 'numeric',
                 // month : '2-digit',
                 // day   : '2-digit',
-                hour  : '2-digit',
+                hour: '2-digit',
                 minute: '2-digit',
                 second: '2-digit'
             };
@@ -283,13 +313,13 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
          *
          * @return {window.Intl.DateTimeFormat}
          */
-        $getDayFormatter: function () {
+        $getDayFormatter: function() {
             let locale = QUILocale.getCurrent();
 
             const options = {
-                year : 'numeric',
+                year: 'numeric',
                 month: '2-digit',
-                day  : '2-digit'
+                day: '2-digit'
             };
 
             if (!locale.match('_')) {
@@ -305,6 +335,95 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
             }
         },
 
+        /**
+         * event: comment click
+         *
+         * @param event
+         */
+        $onEntryClick: function(event) {
+            let Target = event.target;
+
+            if (!Target.hasClass('quiqqer-erp-comments-comment')) {
+                Target = Target.getParent('.quiqqer-erp-comments-comment');
+            }
+
+            switch (Target.get('data-source')) {
+                case 'quiqqer/order':
+                    require([
+                        'package/quiqqer/order/bin/backend/controls/panels/Order'
+                    ], (Order) => {
+                        PanelUtils.openPanelInTasks(
+                            new Order({
+                                orderId: Target.get('data-object-hash')
+                            })
+                        );
+                    });
+                    return;
+
+                case 'quiqqer/invoice':
+                    require([
+                        'package/quiqqer/invoice/bin/backend/controls/panels/Invoice'
+                    ], (Invoice) => {
+                        PanelUtils.openPanelInTasks(
+                            new Invoice({
+                                invoiceId: Target.get('data-object-hash')
+                            })
+                        );
+                    });
+                    return;
+
+                case 'quiqqer/offer':
+                    require([
+                        'package/quiqqer/offers/bin/js/backend/controls/panels/Offer'
+                    ], (Offer) => {
+                        PanelUtils.openPanelInTasks(
+                            new Offer({
+                                offerId: Target.get('data-object-hash')
+                            })
+                        );
+                    });
+                    return;
+
+                case 'quiqqer/purchasing':
+                    require([
+                        'package/quiqqer/purchasing/bin/js/backend/controls/panels/processes/Process'
+                    ], (Process) => {
+                        PanelUtils.openPanelInTasks(
+                            new Process({
+                                processId: Target.get('data-object-hash')
+                            })
+                        );
+                    });
+                    return;
+
+                case 'quiqqer/salesorders':
+                    require([
+                        'package/quiqqer/salesorders/bin/js/backend/controls/panels/SalesOrder'
+                    ], (SalesOrder) => {
+                        PanelUtils.openPanelInTasks(
+                            new SalesOrder({
+                                salesOrderHash: Target.get('data-object-hash')
+                            })
+                        );
+                    });
+                    return;
+
+                case 'quiqqer/payment-transaction':
+                    require([
+                        'package/quiqqer/payment-transactions/bin/backend/controls/windows/Transaction'
+                    ], (TransactionWindow) => {
+                        console.log(Target.get('data-object-hash'));
+
+                        new TransactionWindow({
+                            txid: Target.get('data-object-hash')
+                        }).open();
+                    });
+                    return;
+            }
+
+            QUI.fireEvent('onQuiqqerErpCommentsClick', [this, Target]);
+        },
+
         //region filter
 
         /**
@@ -312,7 +431,7 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
          *
          * @param {String} value
          */
-        filter: function (value) {
+        filter: function(value) {
             this.$filter = value.toString().toLowerCase();
             this.refresh();
         },
@@ -320,7 +439,7 @@ define('package/quiqqer/erp/bin/backend/controls/Comments', [
         /**
          * Clears the filter
          */
-        clearFilter: function () {
+        clearFilter: function() {
             this.$filter = false;
             this.refresh();
         }
diff --git a/bin/backend/controls/articles/Article.js b/bin/backend/controls/articles/Article.js
index 8fe18bcbb4cc7bb72e81a520bfe995fdc0d26933..97e8223c4d707c79ed28f0dc94256137d8685722 100644
--- a/bin/backend/controls/articles/Article.js
+++ b/bin/backend/controls/articles/Article.js
@@ -102,6 +102,8 @@ define('package/quiqqer/erp/bin/backend/controls/articles/Article', [
             'class': 'QUI\\ERP\\Accounting\\Article',
             params: false, // mixed value for API Articles
             currency: false,
+            productSetParentUuid: null,
+            uuid: null,
 
             showSelectCheckbox: false,  // select this article via checkbox instead of click
 
@@ -133,6 +135,14 @@ define('package/quiqqer/erp/bin/backend/controls/articles/Article', [
             this.$calculations = {};
             this.$bruttoCalc = {};
 
+            if (typeof options !== 'undefined' && typeof options.calculated !== 'undefined') {
+                this.$calculations = options.calculated;
+            }
+
+            if (typeof this.$calculations.nettoPriceNotRounded !== 'undefined' && this.$calculations.nettoPriceNotRounded) {
+                this.setAttribute('unitPrice', this.$calculations.nettoPriceNotRounded);
+            }
+
             this.$SelectCheckbox = null;
             this.$Position = null;
             this.$Quantity = null;
diff --git a/bin/backend/controls/articles/ArticleList.js b/bin/backend/controls/articles/ArticleList.js
index c4638e9aab36bfaedfcc8c538c54528224bcfbd0..f52bef81ad3e7e81625cbaf6146ff9102713fa24 100644
--- a/bin/backend/controls/articles/ArticleList.js
+++ b/bin/backend/controls/articles/ArticleList.js
@@ -242,7 +242,7 @@ define('package/quiqqer/erp/bin/backend/controls/articles/ArticleList', [
 
             this.$articles = [];
             let selectedPosition = null;
-
+            console.log(data.articles);
             if (this.$Container) {
                 if (this.$selectedArticle) {
                     selectedPosition = this.$selectedArticle.getAttribute('position');
diff --git a/bin/backend/controls/dashboard/cards/GlobalProcessIdList.js b/bin/backend/controls/dashboard/cards/GlobalProcessIdList.js
new file mode 100644
index 0000000000000000000000000000000000000000..3d7b28c1ee442eb9874f81b7914062d31d4af703
--- /dev/null
+++ b/bin/backend/controls/dashboard/cards/GlobalProcessIdList.js
@@ -0,0 +1,253 @@
+/**
+ * @module package/quiqqer/erp/bin/backend/controls/dashboard/cards/GlobalProcessIdList
+ * @author www.pcsg.de (Henning Leutz)
+ */
+define('package/quiqqer/erp/bin/backend/controls/dashboard/cards/GlobalProcessIdList', [
+
+    'qui/QUI',
+    'package/quiqqer/dashboard/bin/backend/controls/Card',
+    'controls/grid/Grid',
+    'Locale',
+    'Ajax'
+
+], function(QUI, Card, Grid, QUILocale, QUIAjax) {
+    'use strict';
+
+    const lg = 'quiqqer/erp';
+
+    return new Class({
+
+        Extends: Card,
+        Type: 'package/quiqqer/erp/bin/backend/controls/dashboard/cards/GlobalProcessIdList',
+
+        Binds: [
+            'refresh',
+            '$onCreate',
+            '$onDblClick',
+            '$getPlugins'
+        ],
+
+        initialize: function(options) {
+            this.parent(options);
+
+            this.setAttribute({
+                content: '',
+                priority: 1
+            });
+
+            this.$Grid = null;
+
+            this.addEvents({
+                onCreate: this.$onCreate
+            });
+        },
+
+        $onCreate: function() {
+            this.$Content.addClass('card-table');
+            this.$Content.removeClass('card-body');
+
+            this.getElm().classList.add('col-sg-12');
+            this.getElm().classList.add('col-sm-12');
+
+            this.setTitle(QUILocale.get(lg, 'dashboard.erp.grid.title'));
+            this.setContent('');
+
+            const Container = new Element('div', {
+                styles: {
+                    height: 600,
+                    width: '100%'
+                }
+            }).inject(this.getContent());
+
+            this.$getPlugins().then((plugins) => {
+                const columnModel = [];
+
+                columnModel.push({
+                    header: QUILocale.get(lg, 'dashboard.erp.date'),
+                    dataIndex: 'date',
+                    dataType: 'string',
+                    width: 140
+                });
+
+                columnModel.push({
+                    header: QUILocale.get(lg, 'dashboard.erp.processId'),
+                    dataIndex: 'globalProcessId',
+                    dataType: 'string',
+                    width: 240
+                });
+
+                if (plugins.indexOf('quiqqer/invoice') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.invoice'),
+                        dataIndex: 'invoice',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/order') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.order'),
+                        dataIndex: 'order',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/offers') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.offer'),
+                        dataIndex: 'offer',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/salesorders') !== -1) {
+                    console.log(1, QUILocale.get(lg, 'dashboard.erp.salesOrder'));
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.salesOrder'),
+                        dataIndex: 'salesorders',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/purchasing') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.purchasing'),
+                        dataIndex: 'purchasing',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/booking') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.booking'),
+                        dataIndex: 'booking',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/contract') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.contract'),
+                        dataIndex: 'contract',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/delivery-notes') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.deliveryNotes'),
+                        dataIndex: 'deliveryNotes',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                if (plugins.indexOf('quiqqer/payment-transactions') !== -1) {
+                    columnModel.push({
+                        header: QUILocale.get(lg, 'dashboard.erp.transactions'),
+                        dataIndex: 'transactions',
+                        dataType: 'string',
+                        width: 240
+                    });
+                }
+
+                this.$Grid = new Grid(Container, {
+                    buttons: [
+                        {
+                            name: 'add',
+                            textimage: 'fa fa-plus',
+                            text: 'Vorgang hinzufügen',
+                            styles: {
+                                'float': 'right'
+                            }
+                        }
+                    ],
+                    columnModel: columnModel,
+                    pagination: true,
+                    exportData: true
+                });
+
+                this.$Grid.addEvents({
+                    onRefresh: this.refresh,
+                    onDblClick: this.$onDblClick
+                });
+
+                this.$Content.setStyle('padding', 10);
+                this.$Content.setStyle('display', null);
+
+                this.$Grid.setHeight(600);
+                this.refresh();
+            });
+        },
+
+        refresh: function() {
+            if (!this.$Grid) {
+                return Promise.resolve();
+            }
+
+            this.$Grid.showLoader();
+
+            return new Promise((resolve) => {
+                QUIAjax.get('package_quiqqer_erp_ajax_dashboard_globalProcess_getList', (result) => {
+                    let entry;
+                    const data = [];
+                    const DateFormatter = QUILocale.getDateTimeFormatter({
+                        year: 'numeric',
+                        month: '2-digit',
+                        day: '2-digit',
+                        hour: '2-digit',
+                        minute: '2-digit',
+                        second: '2-digit'
+                    });
+
+                    for (let hash in result) {
+                        entry = result[hash];
+                        entry.globalProcessId = hash;
+                        entry.date = DateFormatter.format(new Date(entry.date));
+                        data.push(entry);
+                    }
+
+                    this.$Grid.setData({
+                        data: data
+                    });
+
+                    this.$Grid.hideLoader();
+                    resolve(result);
+                }, {
+                    'package': 'quiqqer/erp'
+                });
+            });
+        },
+
+        $getPlugins: function() {
+            return new Promise((resolve) => {
+                QUIAjax.get('package_quiqqer_erp_ajax_dashboard_globalProcess_availablePlugins', resolve, {
+                    'package': 'quiqqer/erp'
+                });
+            });
+        },
+
+        $onDblClick: function() {
+            const selected = this.$Grid.getSelectedData();
+            const globalProcessId = selected[0].globalProcessId;
+
+            window.parent.require([
+                'utils/Panels',
+                'package/quiqqer/erp/bin/backend/controls/process/ProcessPanel'
+            ], (PanelUtils, ProcessPanel) => {
+                PanelUtils.openPanelInTasks(
+                    new ProcessPanel({
+                        globalProcessId: globalProcessId
+                    })
+                );
+            });
+        }
+    });
+});
diff --git a/bin/backend/controls/elements/PriceCalcInput.js b/bin/backend/controls/elements/PriceCalcInput.js
index 77a6b34f8b1a17829697b18f33d4ae1630aca677..83e4901a47c2247edb0e46cdac4626ddc967d05f 100644
--- a/bin/backend/controls/elements/PriceCalcInput.js
+++ b/bin/backend/controls/elements/PriceCalcInput.js
@@ -1,6 +1,8 @@
 /**
  * @module package/quiqqer/erp/bin/backend/controls/elements/PriceCalcInput
  * @author www.pcsg.de (Henning Leutz)
+ *
+ * @event onSetValue [value, this] - Fires if a net/gross price value is set/calculated
  */
 define('package/quiqqer/erp/bin/backend/controls/elements/PriceCalcInput', [
 
@@ -464,11 +466,13 @@ define('package/quiqqer/erp/bin/backend/controls/elements/PriceCalcInput', [
             if ((foundGroupSeparator || foundDecimalSeparator) &&
                 !(foundGroupSeparator && !foundDecimalSeparator)) {
                 Node.value = value;
+                this.fireEvent('setValue', [value, this]);
                 return Promise.resolve();
             }
 
             return this.getFormatter().then((Formatter) => {
                 Node.value = Formatter.format(parseFloat(value));
+                this.fireEvent('setValue', [parseFloat(value), this]);
             });
         },
 
diff --git a/bin/backend/controls/process/ProcessPanel.css b/bin/backend/controls/process/ProcessPanel.css
new file mode 100644
index 0000000000000000000000000000000000000000..d14c0e78b64818021745fc46f59569dbf989e488
--- /dev/null
+++ b/bin/backend/controls/process/ProcessPanel.css
@@ -0,0 +1,6 @@
+.quiqqer-erp-process-comments-header {
+    background: #f7f7f7;
+    display: inline-block;
+    padding: 20px;
+    width: 100%;
+}
\ No newline at end of file
diff --git a/bin/backend/controls/process/ProcessPanel.js b/bin/backend/controls/process/ProcessPanel.js
new file mode 100644
index 0000000000000000000000000000000000000000..2a8c1df82107b275972242c5e2325fa4f1503843
--- /dev/null
+++ b/bin/backend/controls/process/ProcessPanel.js
@@ -0,0 +1,115 @@
+/**
+ * @module package/quiqqer/erp/bin/backend/controls/process/ProcessPanel
+ * @author www.pcsg.de (Henning Leutz)
+ */
+define('package/quiqqer/erp/bin/backend/controls/process/ProcessPanel', [
+
+    'qui/QUI',
+    'qui/controls/desktop/Panel',
+    'Locale',
+    'Ajax',
+
+    'css!package/quiqqer/erp/bin/backend/controls/process/ProcessPanel.css'
+
+], function(QUI, QUIPanel, QUILocale, QUIAjax) {
+    'use strict';
+
+    const lg = 'quiqqer/erp';
+
+    return new Class({
+
+        Extends: QUIPanel,
+        Type: 'package/quiqqer/erp/bin/backend/controls/process/ProcessPanel',
+
+        Binds: [
+            '$onCreate',
+            '$onShow'
+        ],
+
+        options: {
+            globalProcessId: false
+        },
+
+        initialize: function(options) {
+            this.parent(options);
+
+            if (typeof options.globalProcessId !== 'undefined') {
+                this.setAttribute('#id', 'process--' + options.globalProcessId);
+            }
+
+            this.$Comments = null;
+
+            this.addEvents({
+                onCreate: this.$onCreate,
+                onShow: this.$onShow
+            });
+        },
+
+        $onCreate: function() {
+            if (!this.getAttribute('globalProcessId')) {
+                this.close();
+                return;
+            }
+
+            this.setAttributes({
+                icon: 'fa fa-sitemap',
+                title: QUILocale.get(lg, 'panel.globalProcess.title', {
+                    globalProcessId: this.getAttribute('globalProcessId')
+                })
+            });
+
+            this.Loader.show();
+            this.refresh();
+
+            this.getBody().setStyles({
+                padding: 0
+            });
+
+            new Element('div', {
+                'class': 'quiqqer-erp-process-comments-header',
+                html: '<div class="quiqqer-customer-comments-header-filter">' +
+                    '    <input type="text" name="filter" placeholder="Filter (Nachricht, Type, ID) ...">' +
+                    '  </div>'
+            }).inject(this.getBody());
+
+            require(['package/quiqqer/erp/bin/backend/controls/Comments'], (Comments) => {
+                const CommentContainer = new Element('div', {
+                    styles: {
+                        padding: 20
+                    }
+                }).inject(this.getBody());
+
+                const Filter = this.getBody().getElement('[name="filter"]');
+                this.$Comments = new Comments().inject(CommentContainer);
+
+                Filter.addEvent('keyup', () => {
+                    this.$Comments.filter(Filter.value);
+                });
+
+                this.$onShow();
+            });
+        },
+
+        $onShow: function() {
+            if (!this.getAttribute('globalProcessId')) {
+                this.close();
+                return;
+            }
+
+            if (!this.$Comments) {
+                return;
+            }
+
+            this.Loader.show();
+
+            QUIAjax.get('package_quiqqer_erp_ajax_dashboard_globalProcess_getProcess', (result) => {
+                this.$Comments.clear();
+                this.$Comments.unserialize(result.history);
+                this.Loader.hide();
+            }, {
+                'package': 'quiqqer/erp',
+                globalProcessId: this.getAttribute('globalProcessId')
+            });
+        }
+    });
+});
diff --git a/locale.xml b/locale.xml
index cd237ece29ebbc503a81312dd6626bbcdcb6166d..2d5fbd4aaa0f6a41c43d297101fc721b51624826 100644
--- a/locale.xml
+++ b/locale.xml
@@ -275,7 +275,6 @@
     </groups>
 
     <groups name="quiqqer/erp" datatype="php">
-
         <locale name="bankAccounts.patch.bankAccount_created">
             <de>
                 <![CDATA[Es wurde automatisch ein Standard-Bankkonto auf Basis der Firmen-Bankdaten in der ecoyn-Konfiguration angelegt.]]></de>
@@ -670,11 +669,52 @@
             <de><![CDATA[ecoyn Dashboard]]></de>
             <en><![CDATA[ecoyn dashboard]]></en>
         </locale>
+        <locale name="dashboard.erp.title">
+            <de><![CDATA[ERP Dashboard]]></de>
+            <en><![CDATA[ERP Dashboard]]></en>
+        </locale>
 
         <locale name="exchangerate.text">
             <de><![CDATA[[startCurrency] = [rate]]]></de>
             <en><![CDATA[[startCurrency] = [rate]]]></en>
         </locale>
+        <locale name="created.invoice">
+            <de><![CDATA[Rechnung #[invoiceId] erstellt]]></de>
+            <en><![CDATA[Created invoice #[invoiceId]]]></en>
+        </locale>
+        <locale name="created.order">
+            <de><![CDATA[Bestellung #[orderId] erstellt]]></de>
+            <en><![CDATA[Created order #[orderId]]]></en>
+        </locale>
+
+        <locale name="process.history.invoice.created">
+            <de><![CDATA[Rechnung #[hash] erstellt]]></de>
+            <en><![CDATA[Invoice #[hash] created]]></en>
+        </locale>
+        <locale name="process.history.order.created">
+            <de><![CDATA[Bestellung #[hash] erstellt]]></de>
+            <en><![CDATA[Order #[hash] created]]></en>
+        </locale>
+        <locale name="process.history.offer.created">
+            <de><![CDATA[Angebot #[hash] erstellt]]></de>
+            <en><![CDATA[Offer #[hash] created]]></en>
+        </locale>
+        <locale name="process.history.purchasing.created">
+            <de><![CDATA[Einkauf #[hash] erstellt]]></de>
+            <en><![CDATA[Purchase #[hash] created]]></en>
+        </locale>
+        <locale name="process.history.salesorders.created">
+            <de><![CDATA[Auftrag #[hash] erstellt]]></de>
+            <en><![CDATA[Sales order #[hash] created]]></en>
+        </locale>
+        <locale name="process.history.booking.created">
+            <de><![CDATA[Buchung #[hash] erstellt]]></de>
+            <en><![CDATA[Booking #[hash] created]]></en>
+        </locale>
+        <locale name="process.history.transaction.created">
+            <de><![CDATA[Zahlung von [amount] eingegangen / gebucht. (#[hash])]]></de>
+            <en><![CDATA[Payment from [amount] received / posted. (#[hash])]]></en>
+        </locale>
     </groups>
 
     <groups name="quiqqer/erp" datatype="js">
@@ -1398,5 +1438,58 @@ Allowed characters: Letters, numbers and _ ä ö ü ß]]></en>
             <en><![CDATA[Gross input]]></en>
         </locale>
 
+        <locale name="dashboard.erp.processId">
+            <de><![CDATA[Vorgangsnummer]]></de>
+            <en><![CDATA[Process number]]></en>
+        </locale>
+        <locale name="dashboard.erp.grid.title">
+            <de><![CDATA[Die letzten Vorgänge]]></de>
+            <en><![CDATA[The latest processes]]></en>
+        </locale>
+        <locale name="dashboard.erp.date">
+            <de><![CDATA[Datum]]></de>
+            <en><![CDATA[Date]]></en>
+        </locale>
+        <locale name="dashboard.erp.invoice">
+            <de><![CDATA[Rechnung]]></de>
+            <en><![CDATA[Invoice]]></en>
+        </locale>
+        <locale name="dashboard.erp.order">
+            <de><![CDATA[Bestellung]]></de>
+            <en><![CDATA[Order]]></en>
+        </locale>
+        <locale name="dashboard.erp.offer">
+            <de><![CDATA[Angebot]]></de>
+            <en><![CDATA[Offer]]></en>
+        </locale>
+        <locale name="dashboard.erp.salesOrder">
+            <de><![CDATA[Auftrag]]></de>
+            <en><![CDATA[Sales Order]]></en>
+        </locale>
+        <locale name="dashboard.erp.purchasing">
+            <de><![CDATA[Einkauf]]></de>
+            <en><![CDATA[Purchase]]></en>
+        </locale>
+        <locale name="dashboard.erp.booking">
+            <de><![CDATA[Buchung]]></de>
+            <en><![CDATA[Booking]]></en>
+        </locale>
+        <locale name="dashboard.erp.contract">
+            <de><![CDATA[Vertrag]]></de>
+            <en><![CDATA[Contract]]></en>
+        </locale>
+        <locale name="dashboard.erp.transactions">
+            <de><![CDATA[Zahlungen / Transaktionen]]></de>
+            <en><![CDATA[Payments / Transactions]]></en>
+        </locale>
+        <locale name="dashboard.erp.deliveryNotes">
+            <de><![CDATA[Lieferschein]]></de>
+            <en><![CDATA[Delivery Notes]]></en>
+        </locale>
+        <locale name="panel.globalProcess.title">
+            <de><![CDATA[Prozess: [globalProcessId]]]></de>
+            <en><![CDATA[Process: [globalProcessId]]]></en>
+        </locale>
+
     </groups>
 </locales>
diff --git a/package.xml b/package.xml
index 8c647872e40cb8aaab633945fe71e2b7fb53d252..3ad5c2677709a4eee742db49b94e66e6a25d344f 100644
--- a/package.xml
+++ b/package.xml
@@ -36,7 +36,7 @@
         <provider>
             <erp src="\QUI\ERP\Provider\Erp"/>
             <requirements src="\QUI\ERP\Provider\Requirements"/>
-            <!--            <dashboard src="\QUI\ERP\Dashboard\DashboardProvider"/>-->
+            <dashboard src="\QUI\ERP\Provider\DashboardProvider"/>
         </provider>
     </package>
 </quiqqer>
diff --git a/src/QUI/ERP/Accounting/Article.php b/src/QUI/ERP/Accounting/Article.php
index 5a35b3f651b6997eb7bb4d3d5801fbada50660f6..da134f06cd7b3c8082adf999bad6d6fa9aeb5259 100644
--- a/src/QUI/ERP/Accounting/Article.php
+++ b/src/QUI/ERP/Accounting/Article.php
@@ -121,6 +121,8 @@ class Article implements ArticleInterface
      */
     protected $isNetto;
 
+    protected float $position = 0;
+
     /**
      * @var ArticleDiscount|null
      */
@@ -169,6 +171,9 @@ public function __construct($attributes = [])
         } else {
             $this->attributes['vat'] = '';
         }
+        if (isset($attributes['position'])) {
+            $this->position = (float)$attributes['position'];
+        }
 
         if (isset($attributes['discount'])) {
             $this->Discount = ArticleDiscount::unserialize($attributes['discount']);
@@ -738,6 +743,7 @@ public function toArray(): array
             'class' => $class,
             'customFields' => $this->customFields,
             'customData' => $this->customData,
+            'position' => $this->position,
 
             // calculated data
             'calculated' => [
diff --git a/src/QUI/ERP/Accounting/ArticleListUnique.php b/src/QUI/ERP/Accounting/ArticleListUnique.php
index b767b865d312cda0ae42ec379563b7956b651e51..2cf4db9e56e38ab0fcf16f910b9a407b0b1be79c 100644
--- a/src/QUI/ERP/Accounting/ArticleListUnique.php
+++ b/src/QUI/ERP/Accounting/ArticleListUnique.php
@@ -111,6 +111,10 @@ public function __construct(array $attributes = [], $User = null)
             $currency = $attributes['calculations']['currencyData']['code'];
         }
 
+        // sorting
+        $articles = $this->sortArticlesWithParents($articles);
+
+        // adding
         foreach ($articles as $article) {
             if (!isset($article['currency'])) {
                 $article['currency'] = $currency;
@@ -166,6 +170,63 @@ public function __construct(array $attributes = [], $User = null)
         }
     }
 
+    /**
+     * Sorts items within the list by parent-child relationship.
+     *
+     * Items without `productSetParentUuid` are considered parents and positioned before their children,
+     * with each child directly assigned to its parent via `productSetParentUuid`.
+     *
+     * Children follow immediately after their parents in the sorted list.
+     * Each item is assigned a consecutive position, which reflects its order in the sorted list.
+     *
+     * @param array $articles - The input list of items, articles
+     * @return array The sorted list of items with added 'position' keys, starting with 1.
+     */
+    protected function sortArticlesWithParents(array $articles = []): array
+    {
+        if (empty($articles)) {
+            return [];
+        }
+
+        $sortedArticles = [];
+        $children = [];
+
+        foreach ($articles as $article) {
+            if (!empty($article['productSetParentUuid'])) {
+                $children[$article['productSetParentUuid']][] = $article;
+            }
+        }
+
+        $positionCounter = 1;
+
+        foreach ($articles as $article) {
+            if (!empty($article['productSetParentUuid'])) {
+                continue;
+            }
+
+            if (empty($article['uuid'])) {
+                continue;
+            }
+
+            $article['position'] = $positionCounter;
+            $sortedArticles[] = $article;
+            $uuid = $article['uuid'];
+
+            if (isset($children[$uuid])) {
+                $subPosition = 0.1;
+                foreach ($children[$uuid] as $child) {
+                    $child['position'] = $positionCounter + $subPosition;
+                    $sortedArticles[] = $child;
+                    $subPosition += 0.1;
+                }
+            }
+
+            $positionCounter++;
+        }
+
+        return $sortedArticles;
+    }
+
     /**
      * placeholder. unique list cant be recalculate
      * recalculate makes the unique article list compatible to the article list
@@ -368,17 +429,21 @@ public function toHTML($template = false): string
         $this->calculations['nettoSum'] = $Currency->format($this->calculations['nettoSum']);
         $this->calculations['nettoSubSum'] = $Currency->format($this->calculations['nettoSubSum']);
 
-        $pos = 1;
+        $articles = [];
 
-        $articles = array_map(function ($Article) use ($Currency, &$pos) {
+        foreach ($this->articles as $Article) {
             $View = $Article->getView();
             $View->setCurrency($Currency);
-            $View->setPosition($pos);
+            $position = $View->getPosition();
 
-            $pos++;
+            if (floor($position) % 2) {
+                $View->setAttribute('odd', true);
+            } else {
+                $View->setAttribute('even', true);
+            }
 
-            return $View;
-        }, $this->articles);
+            $articles[] = $View;
+        }
 
         $ExchangeCurrency = $this->ExchangeCurrency;
         $showExchangeRate = $this->showExchangeRate;
@@ -392,8 +457,13 @@ public function toHTML($template = false): string
                 $Currency->setExchangeRate($this->exchangeRate);
             }
 
-            $exchangeRate = $Currency->getExchangeRate($ExchangeCurrency);
-            $exchangeRate = $ExchangeCurrency->format($exchangeRate);
+            if ($Currency instanceof QUI\ERP\CryptoCurrency\Currency) {
+                $ExchangeCurrency->setExchangeRate($this->exchangeRate);
+                $exchangeRate = $Currency->convertFormat(1, $ExchangeCurrency);
+            } else {
+                $exchangeRate = $Currency->getExchangeRate($ExchangeCurrency);
+                $exchangeRate = $ExchangeCurrency->format($exchangeRate);
+            }
 
             $exchangeRateText = $this->Locale->get('quiqqer/erp', 'exchangerate.text', [
                 'startCurrency' => $Currency->format(1),
@@ -404,12 +474,17 @@ public function toHTML($template = false): string
         // if currency of list is other currency like the default one
         // currency = BTC, Default = EUR
         // exchange rate must be displayed
-        if ($ExchangeCurrency && $ExchangeCurrency->getCode() !== QUI\ERP\Defaults::getCurrency()->getCode()) {
+        if ($Currency->getCode() !== QUI\ERP\Defaults::getCurrency()->getCode()) {
             $showExchangeRate = true;
             $DefaultCurrency = QUI\ERP\Defaults::getCurrency();
 
-            $exchangeRate = $ExchangeCurrency->getExchangeRate($DefaultCurrency);
-            $exchangeRate = $ExchangeCurrency->format($exchangeRate);
+            if ($Currency instanceof QUI\ERP\CryptoCurrency\Currency) {
+                $DefaultCurrency->setExchangeRate($this->exchangeRate);
+                $exchangeRate = $Currency->convertFormat(1, $DefaultCurrency);
+            } else {
+                $exchangeRate = $Currency->getExchangeRate($DefaultCurrency);
+                $exchangeRate = $DefaultCurrency->format($exchangeRate);
+            }
 
             $exchangeRateText = $this->Locale->get('quiqqer/erp', 'exchangerate.text', [
                 'startCurrency' => $DefaultCurrency->format(1),
diff --git a/src/QUI/ERP/Accounting/ArticleView.html b/src/QUI/ERP/Accounting/ArticleView.html
index 689d4391c5a4023693bbbcc8ce698eb5858b3b39..01ca1f5d498cda97348d096aa14aa4b0d8d696c1 100644
--- a/src/QUI/ERP/Accounting/ArticleView.html
+++ b/src/QUI/ERP/Accounting/ArticleView.html
@@ -1,4 +1,4 @@
-<tr>
+<tr class="{$cssClasses}">
     <td class="articles-article-pos"
         data-label="{locale group='quiqqer/erp' var='article.list.articles.header.pos'}"
     >
@@ -36,11 +36,11 @@
         </ul>
     </td>
     {if $hasAppliedVat}
-        <td class="articles-article-vat"
-            data-label="{locale group='quiqqer/erp' var='article.list.articles.header.vat'}"
-        >
-            {$this->getAttribute('vat')}%
-        </td>
+    <td class="articles-article-vat"
+        data-label="{locale group='quiqqer/erp' var='article.list.articles.header.vat'}"
+    >
+        {$this->getAttribute('vat')}%
+    </td>
     {/if}
 
     {if $this->getAttribute('quantity') !== false}
diff --git a/src/QUI/ERP/Accounting/ArticleView.php b/src/QUI/ERP/Accounting/ArticleView.php
index a54e40bf111d2368e8fadf47bc60f4440ce42d30..a05a842fa85621b43f3ce109907074659a28143d 100644
--- a/src/QUI/ERP/Accounting/ArticleView.php
+++ b/src/QUI/ERP/Accounting/ArticleView.php
@@ -9,6 +9,8 @@
 use QUI;
 use QUI\ERP\Accounting\Calc as ErpCalc;
 
+use function implode;
+
 /**
  * Class ArticleView
  *
@@ -17,9 +19,9 @@
 class ArticleView extends QUI\QDOM
 {
     /**
-     * @var int
+     * @var float
      */
-    protected int $position;
+    protected float $position;
 
     /**
      * @var Article
@@ -39,6 +41,10 @@ public function __construct(Article $Article)
     {
         $this->Article = $Article;
         $this->setAttributes($this->Article->toArray());
+
+        if ($this->getAttribute('position')) {
+            $this->position = (float)$this->getAttribute('position');
+        }
     }
 
     /**
@@ -54,7 +60,7 @@ public function getQuantityUnit(): string
      *
      * @param QUI\ERP\Currency\Currency $Currency
      */
-    public function setCurrency(QUI\ERP\Currency\Currency $Currency)
+    public function setCurrency(QUI\ERP\Currency\Currency $Currency): void
     {
         $this->Currency = $Currency;
     }
@@ -64,9 +70,17 @@ public function setCurrency(QUI\ERP\Currency\Currency $Currency)
      *
      * @param $position
      */
-    public function setPosition($position)
+    public function setPosition($position): void
+    {
+        $this->position = (float)$position;
+    }
+
+    /**
+     * @return float
+     */
+    public function getPosition(): float
     {
-        $this->position = (int)$position;
+        return $this->position;
     }
 
     /**
@@ -191,9 +205,27 @@ public function toHTML(): string
         }
 
         $articleData = $this->Article->toArray();
+        $cssClasses = ['articles-article-entry'];
+
+        if (!empty($this->Article->getProductSetParentUuid())) {
+            $cssClasses[] = 'articles-article--additional';
+        } else {
+            $cssClasses[] = 'articles-article--real';
+        }
+
+        if ($this->getAttribute('odd')) {
+            $cssClasses[] = 'articles-article--odd';
+        }
+
+        if ($this->getAttribute('even')) {
+            $cssClasses[] = 'articles-article--even';
+        }
 
         $Engine->assign([
             'this' => $this,
+            'uuid' => $articleData['uuid'],
+            'cssClasses' => implode(' ', $cssClasses),
+            'productSetParentUuid' => $articleData['productSetParentUuid'],
             'position' => $this->position,
             'unitPrice' => $Currency->format($article['unitPrice']),
             'sum' => $Currency->format($article['sum']),
diff --git a/src/QUI/ERP/Comments.php b/src/QUI/ERP/Comments.php
index 09f499e029f7bc96d6c404647b8a5d38a8945b9e..e7cd8a974ac288d009f35a950d5d018d42ee892d 100644
--- a/src/QUI/ERP/Comments.php
+++ b/src/QUI/ERP/Comments.php
@@ -119,17 +119,18 @@ public function toArray(): array
      * Add a comment
      *
      * @param string $message
-     * @param int|false $time - optional, unix timestamp
+     * @param bool|int $time - optional, unix timestamp
      * @param string $source - optional, name of the package
      * @param string $sourceIcon - optional, source icon
-     * @param string|false $id - optional, comment id, if needed, it will set one
+     * @param bool|string $id - optional, comment id, if needed, it will set one
      */
     public function addComment(
         string $message,
-        $time = false,
+        bool|int $time = false,
         string $source = '',
         string $sourceIcon = '',
-        $id = false
+        bool|string $id = false,
+        bool|string $objectHash = false
     ) {
         if ($time === false) {
             $time = time();
@@ -151,14 +152,15 @@ public function addComment(
             'time' => (int)$time,
             'source' => $source,
             'sourceIcon' => $sourceIcon,
-            'id' => $id
+            'id' => $id,
+            'objectHash' => $objectHash
         ];
     }
 
     /**
      * Clear all comments
      */
-    public function clear()
+    public function clear(): void
     {
         $this->comments = [];
     }
@@ -166,7 +168,7 @@ public function clear()
     /**
      * Sort all comments via its time
      */
-    public function sort()
+    public function sort(): void
     {
         usort($this->comments, function ($commentA, $commentB) {
             if ($commentA['time'] == $commentB['time']) {
@@ -182,7 +184,7 @@ public function sort()
      *
      * @param Comments $Comments
      */
-    public function import(Comments $Comments)
+    public function import(Comments $Comments): void
     {
         $comments = $Comments->toArray();
 
@@ -204,7 +206,8 @@ public function import(Comments $Comments)
                 $comment['time'],
                 $comment['source'],
                 $comment['sourceIcon'],
-                $comment['id']
+                $comment['id'],
+                $comment['objectHash'] ?? '',
             );
         }
 
@@ -228,7 +231,7 @@ public static function getCommentsByUser(QUI\Users\User $User): ?Comments
 
         if ($User->getAttribute('comments')) {
             $isEditable = QUI\Permissions\Permission::hasPermission('quiqqer.customer.editComments');
-            $json       = json_decode($User->getAttribute('comments'), true);
+            $json = json_decode($User->getAttribute('comments'), true);
 
             if (!is_array($json)) {
                 $json = [];
diff --git a/src/QUI/ERP/Dashboard/Dashboard.php b/src/QUI/ERP/Dashboard/ErpDashboard.php
similarity index 69%
rename from src/QUI/ERP/Dashboard/Dashboard.php
rename to src/QUI/ERP/Dashboard/ErpDashboard.php
index 734f5350d5d606d84d54c39985f1e3eae1fb49f4..053478d8c9f6c2ab81ebeeed4c6c67416414c0a2 100644
--- a/src/QUI/ERP/Dashboard/Dashboard.php
+++ b/src/QUI/ERP/Dashboard/ErpDashboard.php
@@ -10,7 +10,7 @@
  *
  * @package QUI\LoginLogger
  */
-class Dashboard implements DashboardInterface
+class ErpDashboard implements DashboardInterface
 {
     /**
      * @param null $Locale
@@ -22,7 +22,7 @@ public function getTitle($Locale = null): string
             $Locale = QUI::getLocale();
         }
 
-        return $Locale->get('quiqqer/erp', 'dashboard.title');
+        return $Locale->get('quiqqer/erp', 'dashboard.erp.title');
     }
 
     /**
@@ -30,7 +30,9 @@ public function getTitle($Locale = null): string
      */
     public function getCards(): array
     {
-        return [];
+        return [
+            'package/quiqqer/erp/bin/backend/controls/dashboard/cards/GlobalProcessIdList'
+        ];
     }
 
     public function getJavaScriptControl(): string
diff --git a/src/QUI/ERP/Process.php b/src/QUI/ERP/Process.php
index bb5161809bedab46a153b009e78fef5b38c1b8e7..791361ba63f90d8a2f7b609227e270ca707f9b15 100644
--- a/src/QUI/ERP/Process.php
+++ b/src/QUI/ERP/Process.php
@@ -7,12 +7,18 @@
 namespace QUI\ERP;
 
 use QUI;
+use QUI\ERP\Accounting\Offers\Handler as OfferHandler;
+use QUI\ERP\Booking\Table as BookingTable;
+use QUI\ERP\Purchasing\Processes\Handler as PurchasingHandler;
+use QUI\ERP\SalesOrders\Handler as SalesOrdersHandler;
 
 use function count;
+use function strtotime;
 
 /**
  * Class Process
  * - represents a complete erp process
+ * - Vorgangsnummer
  *
  * @package QUI\ERP
  */
@@ -59,9 +65,9 @@ protected function table(): string
      * Add a comment to the history for the complete process
      *
      * @param string $message
-     * @param int|bool $time - optional, unix timestamp
+     * @param bool|int $time - optional, unix timestamp
      */
-    public function addHistory(string $message, $time = false)
+    public function addHistory(string $message, bool|int $time = false): void
     {
         $this->getHistory()->addComment($message, $time);
 
@@ -91,7 +97,7 @@ public function getHistory(): Comments
 
             try {
                 $result = QUI::getDataBase()->fetch([
-                    'from'  => $this->table(),
+                    'from' => $this->table(),
                     'where' => [
                         'id' => $this->processId
                     ],
@@ -125,15 +131,24 @@ public function getCompleteHistory(): Comments
     {
         $History = $this->getHistory();
 
-        $invoices = $this->getInvoices();
-        $orders   = $this->getOrders();
+        $this->parseBookings($History);
+        $this->parseInvoices($History);
+        $this->parseOffers($History);
+        $this->parseOrders($History);
+        $this->parsePurchasing($History);
+        $this->parseSalesOrders($History);
+        $this->parseTransactions($History);
 
-        foreach ($invoices as $Invoice) {
-            $History->import($Invoice->getHistory());
+        try {
+            QUI::getEvents()->fireEvent('quiqqerErpGetCompleteHistory', [$this, $this->processId]);
+        } catch (\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
         }
 
-        foreach ($orders as $Order) {
-            $History->import($Order->getHistory());
+        try {
+            QUI::getEvents()->fireEvent('quiqqerErpProcessHistory', [$this, $this->processId]);
+        } catch (\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
         }
 
         return $History;
@@ -143,6 +158,45 @@ public function getCompleteHistory(): Comments
 
     //region invoice
 
+    protected function parseInvoices(Comments $History): void
+    {
+        $invoices = $this->getInvoices();
+
+        foreach ($invoices as $Invoice) {
+            $History->addComment(
+                QUI::getLocale()->get('quiqqer/erp', 'process.history.invoice.created', [
+                    'hash' => $Invoice->getHash()
+                ]),
+                strtotime($Invoice->getAttribute('date')),
+                'quiqqer/invoice',
+                'fa fa-file-text-o',
+                false,
+                $Invoice->getHash()
+            );
+
+            $history = $Invoice->getHistory()->toArray();
+
+            foreach ($history as $entry) {
+                if (empty($entry['source'])) {
+                    $entry['source'] = 'quiqqer/invoice';
+                }
+
+                if (empty($entry['sourceIcon'])) {
+                    $entry['sourceIcon'] = 'fa fa-file-text-o';
+                }
+
+                $History->addComment(
+                    $entry['message'],
+                    $entry['time'],
+                    $entry['source'],
+                    $entry['sourceIcon'],
+                    $entry['id'],
+                    $Invoice->getHash()
+                );
+            }
+        }
+    }
+
     /**
      * Return if the process has invoices or not
      *
@@ -184,6 +238,10 @@ public function hasTemporaryInvoice(): bool
      */
     public function getInvoices(): array
     {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/invoice')) {
+            return [];
+        }
+
         try {
             return QUI\ERP\Accounting\Invoice\Handler::getInstance()->getInvoicesByGlobalProcessId($this->processId);
         } catch (\QUI\Exception $Exception) {
@@ -195,6 +253,55 @@ public function getInvoices(): array
 
     //region order
 
+    protected function parseOrders(Comments $History): void
+    {
+        // orders
+        $orders = $this->getOrders();
+
+        foreach ($orders as $Order) {
+            $history = $Order->getHistory()->toArray();
+            $hasCreateMessage = false;
+            $createMessage = QUI::getLocale()->get('quiqqer/erp', 'process.history.order.created', [
+                'hash' => $Order->getHash()
+            ]);
+
+            foreach ($history as $entry) {
+                if ($entry['message'] === $createMessage) {
+                    $hasCreateMessage = true;
+                    break;
+                }
+            }
+
+            if ($hasCreateMessage === false) {
+                $History->addComment(
+                    $createMessage,
+                    strtotime($Order->getCreateDate()),
+                    'quiqqer/order',
+                    'fa fa-shopping-basket'
+                );
+            }
+
+            foreach ($history as $entry) {
+                if (empty($entry['source'])) {
+                    $entry['source'] = 'quiqqer/order';
+                }
+
+                if (empty($entry['sourceIcon'])) {
+                    $entry['sourceIcon'] = 'fa fa-shopping-basket';
+                }
+
+                $History->addComment(
+                    $entry['message'],
+                    $entry['time'],
+                    $entry['source'],
+                    $entry['sourceIcon'],
+                    $entry['id'],
+                    $Order->getHash()
+                );
+            }
+        }
+    }
+
     /**
      * @return bool
      */
@@ -208,11 +315,9 @@ public function hasOrder(): bool
      *
      * @return null|Order\Order|Order\OrderInProcess
      */
-    public function getOrder()
+    public function getOrder(): Order\OrderInProcess|Order\Order|null
     {
-        try {
-            QUI::getPackage('quiqqer/order');
-        } catch (QUI\Exception $Exception) {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/order')) {
             return null;
         }
 
@@ -236,13 +341,15 @@ public function getOrder()
     /**
      * Return all orders from the process
      *
-     * @return array|Order\Order|Order\Order[]|Order\OrderInProcess
+     * @return array|Order\Order|Order\Order[]|Order\OrderInProcess[]
      */
-    public function getOrders()
+    public function getOrders(): array
     {
-        try {
-            QUI::getPackage('quiqqer/order');
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/order')) {
+            return [];
+        }
 
+        try {
             return QUI\ERP\Order\Handler::getInstance()->getOrdersByGlobalProcessId($this->processId);
         } catch (QUI\Exception $Exception) {
             return [];
@@ -251,7 +358,340 @@ public function getOrders()
 
     //endregion
 
+    //region offers
+    protected function parseOffers(Comments $History): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/offers')) {
+            return;
+        }
+
+        // orders
+        $offers = $this->getOffers();
+
+        foreach ($offers as $Offer) {
+            $History->addComment(
+                QUI::getLocale()->get('quiqqer/erp', 'process.history.offer.created', [
+                    'hash' => $Offer->getHash()
+                ]),
+                strtotime($Offer->getAttribute('date')),
+                'quiqqer/offer',
+                'fa fa-file-text-o',
+                false,
+                $Offer->getHash()
+            );
+
+            $history = $Offer->getHistory()->toArray();
+
+            foreach ($history as $entry) {
+                if (empty($entry['source'])) {
+                    $entry['source'] = 'quiqqer/offer';
+                }
+
+                if (empty($entry['sourceIcon'])) {
+                    $entry['sourceIcon'] = 'fa fa-file-text-o';
+                }
+
+                $History->addComment(
+                    $entry['message'],
+                    $entry['time'],
+                    $entry['source'],
+                    $entry['sourceIcon'],
+                    $entry['id'],
+                    $Offer->getHash()
+                );
+            }
+        }
+    }
+
+    /**
+     * @return QUI\ERP\Accounting\Offers\Offer[]
+     */
+    public function getOffers(): array
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/offers')) {
+            return [];
+        }
+
+        try {
+            $offers = QUI::getDatabase()->fetch([
+                'select' => 'id,hash,global_process_id,date',
+                'from' => OfferHandler::getInstance()->offersTable(),
+                'where_or' => [
+                    'global_process_id' => $this->processId,
+                    'hash' => $this->processId
+                ]
+            ]);
+        } catch (\Exception) {
+            return [];
+        }
+
+        $result = [];
+        $Offers = OfferHandler::getInstance();
+
+        foreach ($offers as $offer) {
+            try {
+                $result[] = $Offers->getOffer($offer['id']);
+            } catch (\Exception) {
+            }
+        }
+
+        return $result;
+    }
+
+    //endregion
+
+    //region booking
+    protected function parseBookings(Comments $History): void
+    {
+        // orders
+        $bookings = $this->getBookings();
+
+        foreach ($bookings as $Booking) {
+            $History->addComment(
+                QUI::getLocale()->get('quiqqer/erp', 'process.history.booking.created', [
+                    'hash' => $Booking->getUuid()
+                ]),
+                $Booking->getCreateDate()->getTimestamp(),
+                'quiqqer/booking',
+                'fa fa-ticket',
+                false,
+                $Booking->getUuid()
+            );
+
+            $history = $Booking->getHistory()->toArray();
+
+            foreach ($history as $entry) {
+                if (empty($entry['source'])) {
+                    $entry['source'] = 'quiqqer/booking';
+                }
+
+                if (empty($entry['sourceIcon'])) {
+                    $entry['sourceIcon'] = 'fa fa-ticket';
+                }
+
+                $History->addComment(
+                    $entry['message'],
+                    $entry['time'],
+                    $entry['source'],
+                    $entry['sourceIcon'],
+                    $entry['id'],
+                    $Booking->getUuid()
+                );
+            }
+        }
+    }
+
+    /**
+     * @return array
+     */
+    public function getBookings(): array
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/booking')) {
+            return [];
+        }
+
+        try {
+            $bookings = QUI::getDatabase()->fetch([
+                'select' => 'uuid,globalProcessId,createDate',
+                'from' => BookingTable::BOOKINGS->tableName(),
+                'where_or' => [
+                    'globalProcessId' => $this->processId,
+                    'uuid' => $this->processId
+                ]
+            ]);
+        } catch (\Exception) {
+            return [];
+        }
+
+        $result = [];
+        $BookingRepository = new QUI\ERP\Booking\Repository\BookingRepository();
+
+        foreach ($bookings as $booking) {
+            try {
+                $result[] = $BookingRepository->getByUuid($booking['uuid']);
+            } catch (\Exception) {
+            }
+        }
+
+        return $result;
+    }
+
+    //endregion
+
+    //region purchase / Einkauf
+    protected function parsePurchasing(Comments $History): void
+    {
+        // orders
+        $purchasing = $this->getPurchasing();
+
+        foreach ($purchasing as $Purchasing) {
+            $History->addComment(
+                QUI::getLocale()->get('quiqqer/erp', 'process.history.purchasing.created', [
+                    'hash' => $Purchasing->getHash()
+                ]),
+                strtotime($Purchasing->getAttribute('c_date')),
+                'quiqqer/purchasing',
+                'fa fa-cart-arrow-down',
+                false,
+                $Purchasing->getHash()
+            );
+
+            $history = $Purchasing->getHistory()->toArray();
+
+            foreach ($history as $entry) {
+                if (empty($entry['source'])) {
+                    $entry['source'] = 'quiqqer/purchasing';
+                }
+
+                if (empty($entry['sourceIcon'])) {
+                    $entry['sourceIcon'] = 'fa fa-cart-arrow-down';
+                }
+
+                $History->addComment(
+                    $entry['message'],
+                    $entry['time'],
+                    $entry['source'],
+                    $entry['sourceIcon'],
+                    $entry['id'],
+                    $Purchasing->getHash()
+                );
+            }
+        }
+    }
+
+    /**
+     * @return QUI\ERP\Purchasing\Processes\PurchasingProcess[]
+     */
+    public function getPurchasing(): array
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/purchasing')) {
+            return [];
+        }
+
+        try {
+            $purchasing = QUI::getDatabase()->fetch([
+                'select' => 'id,hash,global_process_id,date',
+                'from' => PurchasingHandler::getTablePurchasingProcesses(),
+                'where_or' => [
+                    'global_process_id' => $this->processId,
+                    'hash' => $this->processId
+                ]
+            ]);
+        } catch (\Exception) {
+            return [];
+        }
+
+        $result = [];
+
+        foreach ($purchasing as $process) {
+            try {
+                $result[] = PurchasingHandler::getPurchasingProcess($process['id']);
+            } catch (\Exception) {
+            }
+        }
+
+        return $result;
+    }
+
+    //endregion
+
+    //region sales orders / Aufträge
+    protected function parseSalesOrders(Comments $History): void
+    {
+        // orders
+        $salesOrders = $this->getSalesOrders();
+
+        foreach ($salesOrders as $SalesOrder) {
+            $History->addComment(
+                QUI::getLocale()->get('quiqqer/erp', 'process.history.salesorders.created', [
+                    'hash' => $SalesOrder->getHash()
+                ]),
+                strtotime($SalesOrder->getAttribute('c_date')),
+                'quiqqer/salesorders',
+                'fa fa-suitcase',
+                false,
+                $SalesOrder->getHash()
+            );
+
+            $history = $SalesOrder->getHistory()->toArray();
+
+            foreach ($history as $entry) {
+                if (empty($entry['source'])) {
+                    $entry['source'] = 'quiqqer/salesorders';
+                }
+
+                if (empty($entry['sourceIcon'])) {
+                    $entry['sourceIcon'] = 'fa fa-suitcase';
+                }
+
+                $History->addComment(
+                    $entry['message'],
+                    $entry['time'],
+                    $entry['source'],
+                    $entry['sourceIcon'],
+                    $entry['id'],
+                    $SalesOrder->getHash()
+                );
+            }
+        }
+    }
+
+    /**
+     * @return QUI\ERP\Purchasing\Processes\PurchasingProcess[]
+     */
+    public function getSalesOrders(): array
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/salesorders')) {
+            return [];
+        }
+
+        try {
+            $salesOrders = QUI::getDatabase()->fetch([
+                'select' => 'id,hash,global_process_id,date',
+                'from' => SalesOrdersHandler::getTableSalesOrders(),
+                'where_or' => [
+                    'global_process_id' => $this->processId,
+                    'hash' => $this->processId
+                ]
+            ]);
+        } catch (\Exception) {
+            return [];
+        }
+
+        $result = [];
+
+        foreach ($salesOrders as $salesOrder) {
+            try {
+                $result[] = SalesOrdersHandler::getSalesOrder($salesOrder['id']);
+            } catch (\Exception) {
+            }
+        }
+
+        return $result;
+    }
+
+    //endregion
+
     //region transactions
+    protected function parseTransactions(Comments $History): void
+    {
+        // orders
+        $transactions = $this->getTransactions();
+
+        foreach ($transactions as $Transaction) {
+            $History->addComment(
+                QUI::getLocale()->get('quiqqer/erp', 'process.history.transaction.created', [
+                    'hash' => $Transaction->getHash(),
+                    'amount' => $Transaction->getAmountFormatted()
+                ]),
+                strtotime($Transaction->getDate()),
+                'quiqqer/payment-transaction',
+                'fa fa-money',
+                false,
+                $Transaction->getHash()
+            );
+        }
+    }
 
     /**
      * @return bool
@@ -268,11 +708,9 @@ public function hasTransactions(): bool
      *
      * @return QUI\ERP\Accounting\Payments\Transactions\Transaction[];
      */
-    public function getTransactions(): ?array
+    public function getTransactions(): array
     {
-        try {
-            QUI::getPackage('quiqqer/payment-transactions');
-        } catch (QUI\Exception $Exception) {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/payment-transactions')) {
             return [];
         }
 
@@ -280,7 +718,7 @@ public function getTransactions(): ?array
             return $this->transactions;
         }
 
-        $Transactions       = QUI\ERP\Accounting\Payments\Transactions\Handler::getInstance();
+        $Transactions = QUI\ERP\Accounting\Payments\Transactions\Handler::getInstance();
         $this->transactions = $Transactions->getTransactionsByProcessId($this->processId);
 
         return $this->transactions;
diff --git a/src/QUI/ERP/Processes.php b/src/QUI/ERP/Processes.php
new file mode 100644
index 0000000000000000000000000000000000000000..0047273ac2171fccb64daec9958b0490aadc856f
--- /dev/null
+++ b/src/QUI/ERP/Processes.php
@@ -0,0 +1,393 @@
+<?php
+
+/**
+ * This file contains QUI\ERP\Processes
+ */
+
+namespace QUI\ERP;
+
+use QUI;
+use QUI\ERP\Accounting\Invoice\Handler as InvoiceHandler;
+use QUI\ERP\Accounting\Offers\Handler as OfferHandler;
+use QUI\ERP\Accounting\Payments\Transactions\Factory as TransactionFactory;
+use QUI\ERP\Booking\Table as BookingTable;
+use QUI\ERP\Order\Handler as OrderHandler;
+use QUI\ERP\Purchasing\Processes\Handler as PurchasingHandler;
+use QUI\ERP\SalesOrders\Handler as SalesOrdersHandler;
+
+/**
+ *
+ * Reads following modules for global process id entities
+ * - quiqqer/invoice
+ * - quiqqer/order
+ * - quiqqer/offer
+ * - quiqqer/salesorders
+ * - quiqqer/purchasing
+ * - quiqqer/booking
+ * - quiqqer/contracts
+ * - quiqqer/payments
+ * - quiqqer/payment-transactions
+ * - quiqqer/delivery-notes
+ */
+class Processes
+{
+    protected array $list = [];
+
+    /**
+     * @return array
+     */
+    public function getList(): array
+    {
+        try {
+            $this->readBooking();
+        } catch (QUI\Database\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
+        }
+
+        try {
+            $this->readInvoices();
+        } catch (QUI\Database\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
+        }
+
+        try {
+            $this->readOffers();
+        } catch (QUI\Database\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
+        }
+
+        try {
+            $this->readOrders();
+        } catch (QUI\Database\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
+        }
+
+        try {
+            $this->readPurchasing();
+        } catch (QUI\Database\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
+        }
+
+        try {
+            $this->readSalesOrders();
+        } catch (QUI\Database\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
+        }
+
+        try {
+            $this->readTransactions();
+        } catch (QUI\Database\Exception $exception) {
+            QUI\System\Log::addError($exception->getMessage());
+        }
+
+        uasort($this->list, function ($a, $b) {
+            return strtotime($b['date']) - strtotime($a['date']);
+        });
+
+        return $this->list;
+    }
+
+    /**
+     * Returns the plugin list with which plugins the processes can handle
+     *
+     * @return string[]
+     */
+    public function getWantedPluginList(): array
+    {
+        return [
+            'quiqqer/booking',
+            'quiqqer/contracts',
+            'quiqqer/delivery-notes',
+            'quiqqer/invoice',
+            'quiqqer/offers',
+            'quiqqer/order',
+            'quiqqer/payments',
+            'quiqqer/payment-transactions',
+            'quiqqer/purchasing',
+            'quiqqer/salesorders'
+        ];
+    }
+
+    //region read databases
+
+    /**
+     * Rads all invoices
+     *
+     * @return void
+     * @throws QUI\Database\Exception
+     */
+    protected function readInvoices(): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/invoice')) {
+            return;
+        }
+
+        $invoices = QUI::getDatabase()->fetch([
+            'select' => 'hash,global_process_id,date',
+            'from' => InvoiceHandler::getInstance()->invoiceTable()
+        ]);
+
+        foreach ($invoices as $invoice) {
+            $gpi = $invoice['global_process_id'];
+
+            if (empty($gpi)) {
+                $gpi = $invoice['hash'];
+            }
+
+            if (!isset($this->list[$gpi])) {
+                $this->list[$gpi] = [];
+            }
+
+            $this->list[$gpi]['date'] = $this->getEarlierDate(
+                $this->list[$gpi]['date'] ?? null,
+                $invoice['date']
+            );
+
+            $this->list[$gpi]['invoice'] = $invoice['hash'];
+        }
+    }
+
+    /**
+     * Rads all invoices
+     *
+     * @return void
+     * @throws QUI\Database\Exception
+     */
+    protected function readOrders(): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/order')) {
+            return;
+        }
+
+        $orders = QUI::getDatabase()->fetch([
+            'select' => 'hash,global_process_id,c_date',
+            'from' => OrderHandler::getInstance()->table()
+        ]);
+
+        foreach ($orders as $order) {
+            $gpi = $order['global_process_id'];
+
+            if (empty($gpi)) {
+                $gpi = $order['hash'];
+            }
+
+            if (!isset($this->list[$gpi])) {
+                $this->list[$gpi] = [];
+            }
+
+            $this->list[$gpi]['date'] = $this->getEarlierDate(
+                $this->list[$gpi]['date'] ?? null,
+                $order['c_date']
+            );
+
+            $this->list[$gpi]['order'] = $order['hash'];
+        }
+    }
+
+    /**
+     * Read all offers
+     *
+     * @return void
+     * @throws QUI\Database\Exception
+     */
+    protected function readOffers(): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/offers')) {
+            return;
+        }
+
+        $offers = QUI::getDatabase()->fetch([
+            'select' => 'hash,global_process_id,date',
+            'from' => OfferHandler::getInstance()->offersTable()
+        ]);
+
+        foreach ($offers as $offer) {
+            $gpi = $offer['global_process_id'];
+
+            if (empty($gpi)) {
+                $gpi = $offer['hash'];
+            }
+
+            if (!isset($this->list[$gpi])) {
+                $this->list[$gpi] = [];
+            }
+
+            $this->list[$gpi]['date'] = $this->getEarlierDate(
+                $this->list[$gpi]['date'] ?? null,
+                $offer['date']
+            );
+
+            $this->list[$gpi]['offer'] = $offer['hash'];
+        }
+    }
+
+    /**
+     * Read all sales orders
+     *
+     * @return void
+     * @throws QUI\Database\Exception
+     */
+    protected function readSalesOrders(): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/salesorders')) {
+            return;
+        }
+
+        $salesOrders = QUI::getDatabase()->fetch([
+            'select' => 'hash,global_process_id,date',
+            'from' => SalesOrdersHandler::getTableSalesOrders()
+        ]);
+
+        foreach ($salesOrders as $salesOrder) {
+            $gpi = $salesOrder['global_process_id'];
+
+            if (empty($gpi)) {
+                $gpi = $salesOrder['hash'];
+            }
+
+            if (!isset($this->list[$gpi])) {
+                $this->list[$gpi] = [];
+            }
+
+            $this->list[$gpi]['date'] = $this->getEarlierDate(
+                $this->list[$gpi]['date'] ?? null,
+                $salesOrder['date']
+            );
+
+            $this->list[$gpi]['salesorders'] = $salesOrder['hash'];
+        }
+    }
+
+    /**
+     * Read all sales orders
+     *
+     * @return void
+     * @throws QUI\Database\Exception
+     */
+    protected function readTransactions(): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/payment-transactions')) {
+            return;
+        }
+
+        $transactions = QUI::getDatabase()->fetch([
+            'select' => 'hash,global_process_id,date',
+            'from' => TransactionFactory::table()
+        ]);
+
+        foreach ($transactions as $transaction) {
+            $gpi = $transaction['global_process_id'];
+
+            if (empty($gpi)) {
+                $gpi = $transaction['hash'];
+            }
+
+            if (!isset($this->list[$gpi])) {
+                $this->list[$gpi] = [];
+            }
+
+            $this->list[$gpi]['date'] = $this->getEarlierDate(
+                $this->list[$gpi]['date'] ?? null,
+                $transaction['date']
+            );
+
+            $this->list[$gpi]['transactions'] = $transaction['hash'];
+        }
+    }
+
+    /**
+     * Read all purchases
+     *
+     * @return void
+     * @throws QUI\Database\Exception
+     */
+    protected function readPurchasing(): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/purchasing')) {
+            return;
+        }
+
+        $purchasing = QUI::getDatabase()->fetch([
+            'select' => 'hash,global_process_id,date',
+            'from' => PurchasingHandler::getTablePurchasingProcesses()
+        ]);
+
+        foreach ($purchasing as $entry) {
+            $gpi = $entry['global_process_id'];
+
+            if (empty($gpi)) {
+                $gpi = $entry['hash'];
+            }
+
+            if (!isset($this->list[$gpi])) {
+                $this->list[$gpi] = [];
+            }
+
+            $this->list[$gpi]['date'] = $this->getEarlierDate(
+                $this->list[$gpi]['date'] ?? null,
+                $entry['date']
+            );
+
+            $this->list[$gpi]['purchasing'] = $entry['hash'];
+        }
+    }
+
+    /**
+     * Read all purchases
+     *
+     * @return void
+     * @throws QUI\Database\Exception
+     */
+    protected function readBooking(): void
+    {
+        if (!QUI::getPackageManager()->isInstalled('quiqqer/booking')) {
+            return;
+        }
+
+        $bookings = QUI::getDatabase()->fetch([
+            'select' => 'uuid,globalProcessId,createDate',
+            'from' => BookingTable::BOOKINGS->tableName(),
+        ]);
+
+        foreach ($bookings as $booking) {
+            $gpi = $booking['globalProcessId'];
+
+            if (empty($gpi)) {
+                $gpi = $booking['uuid'];
+            }
+
+            if (!isset($this->list[$gpi])) {
+                $this->list[$gpi] = [];
+            }
+
+            $this->list[$gpi]['date'] = $this->getEarlierDate(
+                $this->list[$gpi]['date'] ?? null,
+                $booking['createDate']
+            );
+
+            $this->list[$gpi]['booking'] = $booking['uuid'];
+        }
+    }
+
+    //endregion
+
+    //region utils
+
+    protected function getEarlierDate($date1, $date2)
+    {
+        $timestamp1 = strtotime($date1);
+        $timestamp2 = strtotime($date2);
+
+        if ($date1 === null && $date2) {
+            return $date2;
+        }
+
+        if ($date1 && $date2 === null) {
+            return $date1;
+        }
+
+        return ($timestamp1 < $timestamp2) ? $date1 : $date2;
+    }
+
+    //endregion
+}
diff --git a/src/QUI/ERP/Dashboard/DashboardProvider.php b/src/QUI/ERP/Provider/DashboardProvider.php
similarity index 53%
rename from src/QUI/ERP/Dashboard/DashboardProvider.php
rename to src/QUI/ERP/Provider/DashboardProvider.php
index eddc64e2c120d34f30a61ac3192def07f6457238..71ae31a1ec02fe08d118fa46acfaf1fc9b631e25 100644
--- a/src/QUI/ERP/Dashboard/DashboardProvider.php
+++ b/src/QUI/ERP/Provider/DashboardProvider.php
@@ -1,24 +1,19 @@
 <?php
 
-namespace QUI\ERP\Dashboard;
+namespace QUI\ERP\Provider;
 
+use QUI;
 use QUI\Dashboard\DashboardProviderInterface;
 
-/**
- * Class DashboardProvider
- *
- * @package QUI\LoginLogger
- */
 class DashboardProvider implements DashboardProviderInterface
 {
-    /**
-     * @inheritDoc
-     *
-     * @return array
-     */
-    public static function getCards(): array
+    public function getTitle($Locale = null): string
     {
-        return [];
+        if ($Locale === null) {
+            $Locale = QUI::getLocale();
+        }
+
+        return $Locale->get('quiqqer/erp', 'dashboard.provider.title');
     }
 
     /**
@@ -27,7 +22,15 @@ public static function getCards(): array
     public static function getBoards(): array
     {
         return [
-            new Dashboard()
+            new QUI\ERP\Dashboard\ErpDashboard()
         ];
     }
+
+    /**
+     * @return array
+     */
+    public static function getCards(): array
+    {
+        return [];
+    }
 }
diff --git a/src/QUI/ERP/Utils/Process.php b/src/QUI/ERP/Utils/Process.php
index dacf1e3f13e1159fda0c6db1cc18730d74c90465..77c82775fa8974f13e8894ad5546187e60f4d8df 100644
--- a/src/QUI/ERP/Utils/Process.php
+++ b/src/QUI/ERP/Utils/Process.php
@@ -22,6 +22,8 @@ class Process
      *
      * @param string $hash - process hash
      * @return array
+     *
+     * @deprecated
      */
     public static function getProcessInformation($hash)
     {
@@ -31,7 +33,7 @@ public static function getProcessInformation($hash)
         try {
             QUI::getPackage('quiqqer/order');
 
-            $Order           = QUI\ERP\Order\Handler::getInstance()->getOrderByHash($hash);
+            $Order = QUI\ERP\Order\Handler::getInstance()->getOrderByHash($hash);
             $result['order'] = $Order->getAttributes();
         } catch (QUI\Exception $Exception) {
             QUI\System\Log::writeDebugException($Exception);
@@ -41,7 +43,7 @@ public static function getProcessInformation($hash)
         try {
             QUI::getPackage('quiqqer/invoice');
 
-            $Invoice           = QUI\ERP\Accounting\Invoice\Utils\Invoice::getInvoiceByString($hash);
+            $Invoice = QUI\ERP\Accounting\Invoice\Utils\Invoice::getInvoiceByString($hash);
             $result['invoice'] = $Invoice->getAttributes();
         } catch (QUI\Exception $Exception) {
             QUI\System\Log::writeDebugException($Exception);