From 00c4792c17c74425615bf15785f4f3a2a7e765de Mon Sep 17 00:00:00 2001 From: data Date: Thu, 19 Feb 2026 20:23:09 +0100 Subject: [PATCH] v1.2: Bugfixes USt-Widget, Statusfilter und Kategoriestatistik - USt-Widget: Spalte fd.total_tva -> fd.tva fuer Lieferantenrechnungen (facture_fourn_det nutzt 'tva' statt 'total_tva' wie facturedet) - Stornierte Rechnungen (Status 3) aus allen Berechnungen ausgeschlossen (fk_statut > 0 -> fk_statut IN (1, 2) in 10 SQL-Queries) - Kategoriestatistik: Jahresvergleich berechnet jetzt korrekt zum Vorjahr - invoice_category_stats.php: Robuste Pfaderkennung statt hartem require - Fehlende en_US Uebersetzungen fuer Kategoriestatistik ergaenzt - Version 1.1 -> 1.2 Co-Authored-By: Claude Opus 4.6 --- ChangeLog.md | 10 +++ README.md | 9 ++- core/boxes/box_gewinn_verlust.php | 4 +- core/boxes/box_rentabilitaet.php | 4 +- core/boxes/box_ust_uebersicht.php | 7 +- core/modules/modBuchaltungsWidget.class.php | 2 +- gewinn_detail.php | 4 +- invoice_category_stats.php | 83 ++++++++++++++------- langs/en_US/buchaltungswidget.lang | 14 ++++ rentabilitaet_detail.php | 4 +- ust_detail.php | 7 +- 11 files changed, 106 insertions(+), 42 deletions(-) mode change 100644 => 100755 invoice_category_stats.php diff --git a/ChangeLog.md b/ChangeLog.md index ec3db4e..9c14881 100755 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,15 @@ # CHANGELOG MODULE BUCHALTUNGSWIDGET FOR [DOLIBARR ERP CRM](https://www.dolibarr.org) +## 1.2 + +- Bugfix: USt-Widget zeigte bezahlte USt (Vorsteuer) immer als 0 an + - Ursache: Falsche Spalte `total_tva` statt `tva` in Tabelle `facture_fourn_det` +- Bugfix: Stornierte Rechnungen (Status 3) wurden in allen Berechnungen mitgezaehlt + - Betrifft: USt, Gewinn/Verlust, Rentabilitaet (Widgets + Detailseiten) +- Bugfix: Jahresvergleich in Kategoriestatistik berechnete Prozente in falscher Richtung +- Bugfix: Robuste Pfaderkennung fuer invoice_category_stats.php +- Fehlende englische Uebersetzungen fuer Kategoriestatistik ergaenzt + ## 1.1 - Neue Statistikseite: Rechnungsstatistik nach Kategorie diff --git a/README.md b/README.md index 125e8a9..bd73973 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # BUCHHALTUNGS-WIDGET / ACCOUNTING WIDGETS FOR [DOLIBARR ERP & CRM](https://www.dolibarr.org) -**Version:** 1.1 +**Version:** 1.2 **Compatibility:** Dolibarr 19.0+ **Author:** Eduard Wisch - Data IT Solution **License:** GPL v3+ @@ -157,6 +157,13 @@ The following options can be configured in the admin area: ## Changelog +### Version 1.2 +- Fix: VAT widget showed paid VAT (input tax) always as 0 (wrong column name in supplier invoice detail table) +- Fix: Cancelled invoices (status 3) were included in all financial calculations +- Fix: Year comparison percentages in category statistics were calculated in wrong direction +- Fix: Robust path detection for invoice_category_stats.php +- Added missing English translations for category statistics + ### Version 1.1 - New: Invoice Category Statistics page - Filter invoices by category/tag diff --git a/core/boxes/box_gewinn_verlust.php b/core/boxes/box_gewinn_verlust.php index 6868a1b..3e33337 100755 --- a/core/boxes/box_gewinn_verlust.php +++ b/core/boxes/box_gewinn_verlust.php @@ -289,7 +289,7 @@ class box_gewinn_verlust extends ModeleBoxes // Income from customer invoices $sql = "SELECT MONTH(f.datef) as month, SUM(f.total_ht) as total"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY MONTH(f.datef)"; @@ -309,7 +309,7 @@ class box_gewinn_verlust extends ModeleBoxes $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn_det as fd ON fd.fk_facture_fourn = f.rowid"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = fd.fk_product"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); // Only include products (materials) - product_type 0 = product, 1 = service // Also include invoice lines with product linked diff --git a/core/boxes/box_rentabilitaet.php b/core/boxes/box_rentabilitaet.php index f849083..e2c5946 100755 --- a/core/boxes/box_rentabilitaet.php +++ b/core/boxes/box_rentabilitaet.php @@ -302,7 +302,7 @@ class box_rentabilitaet extends ModeleBoxes $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn_det as fd ON fd.fk_facture_fourn = f.rowid"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = fd.fk_product"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); // Only products (type 0), not services (type 1) // And only products that are meant for resale or customer projects @@ -322,7 +322,7 @@ class box_rentabilitaet extends ModeleBoxes $sql = "SELECT MONTH(f.datef) as month, SUM(fd.total_ht) as total"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facturedet as fd ON fd.fk_facture = f.rowid"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY MONTH(f.datef)"; diff --git a/core/boxes/box_ust_uebersicht.php b/core/boxes/box_ust_uebersicht.php index 5f6fcf6..3063341 100755 --- a/core/boxes/box_ust_uebersicht.php +++ b/core/boxes/box_ust_uebersicht.php @@ -250,7 +250,7 @@ class box_ust_uebersicht extends ModeleBoxes $sql = "SELECT QUARTER(f.datef) as quarter, SUM(fd.total_tva) as tva_amount"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facturedet as fd ON fd.fk_facture = f.rowid"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY QUARTER(f.datef)"; @@ -265,10 +265,11 @@ class box_ust_uebersicht extends ModeleBoxes } // VAT paid from supplier invoices - $sql = "SELECT QUARTER(f.datef) as quarter, SUM(fd.total_tva) as tva_amount"; + // Hinweis: In facture_fourn_det heißt die USt-Spalte 'tva', nicht 'total_tva' wie bei facturedet + $sql = "SELECT QUARTER(f.datef) as quarter, SUM(fd.tva) as tva_amount"; $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn_det as fd ON fd.fk_facture_fourn = f.rowid"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY QUARTER(f.datef)"; diff --git a/core/modules/modBuchaltungsWidget.class.php b/core/modules/modBuchaltungsWidget.class.php index ebef7fa..a9e36b0 100755 --- a/core/modules/modBuchaltungsWidget.class.php +++ b/core/modules/modBuchaltungsWidget.class.php @@ -76,7 +76,7 @@ class modBuchaltungsWidget extends DolibarrModules $this->editor_squarred_logo = ''; // Must be image filename into the module/img directory followed with @modulename. Example: 'myimage.png@buchaltungswidget' // Possible values for version are: 'development', 'experimental', 'dolibarr', 'dolibarr_deprecated', 'experimental_deprecated' or a version string like 'x.y.z' - $this->version = '1.1'; + $this->version = '1.2'; // Url to the file with your last numberversion of this module //$this->url_last_version = 'http://www.example.com/versionmodule.txt'; diff --git a/gewinn_detail.php b/gewinn_detail.php index 8fb1a74..dfcb37f 100755 --- a/gewinn_detail.php +++ b/gewinn_detail.php @@ -320,7 +320,7 @@ function getIncomeExpenseByMonth($db, $year) // Income from customer invoices $sql = "SELECT MONTH(f.datef) as month, SUM(f.total_ht) as total"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY MONTH(f.datef)"; @@ -337,7 +337,7 @@ function getIncomeExpenseByMonth($db, $year) $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn_det as fd ON fd.fk_facture_fourn = f.rowid"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = fd.fk_product"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " AND (fd.fk_product IS NOT NULL AND fd.fk_product > 0)"; $sql .= " AND (p.fk_product_type = 0 OR fd.product_type = 0)"; diff --git a/invoice_category_stats.php b/invoice_category_stats.php old mode 100644 new mode 100755 index 385ad5d..755f8b7 --- a/invoice_category_stats.php +++ b/invoice_category_stats.php @@ -14,7 +14,37 @@ */ // Load Dolibarr environment -require '../../main.inc.php'; +$res = 0; +if (!$res && !empty($_SERVER["CONTEXT_DOCUMENT_ROOT"])) { + $res = @include $_SERVER["CONTEXT_DOCUMENT_ROOT"]."/main.inc.php"; +} +$tmp = empty($_SERVER['SCRIPT_FILENAME']) ? '' : $_SERVER['SCRIPT_FILENAME']; +$tmp2 = realpath(__FILE__); +$i = strlen($tmp) - 1; +$j = strlen($tmp2) - 1; +while ($i > 0 && $j > 0 && isset($tmp[$i]) && isset($tmp2[$j]) && $tmp[$i] == $tmp2[$j]) { + $i--; + $j--; +} +if (!$res && $i > 0 && file_exists(substr($tmp, 0, ($i + 1))."/main.inc.php")) { + $res = @include substr($tmp, 0, ($i + 1))."/main.inc.php"; +} +if (!$res && $i > 0 && file_exists(dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php")) { + $res = @include dirname(substr($tmp, 0, ($i + 1)))."/main.inc.php"; +} +if (!$res && file_exists("../main.inc.php")) { + $res = @include "../main.inc.php"; +} +if (!$res && file_exists("../../main.inc.php")) { + $res = @include "../../main.inc.php"; +} +if (!$res && file_exists("../../../main.inc.php")) { + $res = @include "../../../main.inc.php"; +} +if (!$res) { + die("Include of main fails"); +} + require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php'; @@ -382,39 +412,40 @@ function getInvoiceStatsByYear($db, $conf, $socid, $userid, $object_status, $sql $resql = $db->query($sql); if ($resql) { - $prevnb = 0; - $prevtotal = 0; - $prevavg = 0; - + // Zuerst alle Zeilen einlesen (absteigend sortiert) + $rows = array(); while ($obj = $db->fetch_object($resql)) { - $row = array( + $rows[] = array( 'year' => $obj->year, 'nb' => (int) $obj->nb, 'total' => (float) $obj->total, 'avg' => (float) $obj->avg, - 'nb_diff' => 0, - 'total_diff' => 0, - 'avg_diff' => 0, ); - - // Calculate percentage differences - if ($prevnb > 0) { - $row['nb_diff'] = (($obj->nb - $prevnb) / $prevnb) * 100; - } - if ($prevtotal > 0) { - $row['total_diff'] = (($obj->total - $prevtotal) / $prevtotal) * 100; - } - if ($prevavg > 0) { - $row['avg_diff'] = (($obj->avg - $prevavg) / $prevavg) * 100; - } - - $data[] = $row; - - $prevnb = $obj->nb; - $prevtotal = $obj->total; - $prevavg = $obj->avg; } $db->free($resql); + + // Prozentuale Veraenderung zum Vorjahr berechnen (jedes Jahr mit dem aelteren vergleichen) + for ($i = 0; $i < count($rows); $i++) { + $rows[$i]['nb_diff'] = 0; + $rows[$i]['total_diff'] = 0; + $rows[$i]['avg_diff'] = 0; + + // Das naechste Element im Array ist das aeltere Jahr (DESC-Sortierung) + if (isset($rows[$i + 1])) { + $prev = $rows[$i + 1]; + if ($prev['nb'] > 0) { + $rows[$i]['nb_diff'] = (($rows[$i]['nb'] - $prev['nb']) / $prev['nb']) * 100; + } + if ($prev['total'] > 0) { + $rows[$i]['total_diff'] = (($rows[$i]['total'] - $prev['total']) / $prev['total']) * 100; + } + if ($prev['avg'] > 0) { + $rows[$i]['avg_diff'] = (($rows[$i]['avg'] - $prev['avg']) / $prev['avg']) * 100; + } + } + } + + $data = $rows; } return $data; diff --git a/langs/en_US/buchaltungswidget.lang b/langs/en_US/buchaltungswidget.lang index 1ebe661..3cea0da 100755 --- a/langs/en_US/buchaltungswidget.lang +++ b/langs/en_US/buchaltungswidget.lang @@ -122,6 +122,20 @@ PaymentWarning = Slow Payer PaymentLate = Late PaymentCritical = Critical +# +# Invoice Category Statistics +# +StatistikenNachKategorie = Statistics by Category +NachRechnungskategorie = By Invoice Category +Statistik = Statistics +InvoiceCategoryStatistics = Invoice Statistics by Category +SelectInvoiceCategory = Select Invoice Category +NoInvoiceCategoriesFound = No invoice categories found +AllCategories = All Categories +CategoryFilter = Category Filter +InvoicesInCategory = Invoices in this Category +AmountInCategory = Amount in this Category + # # Months # diff --git a/rentabilitaet_detail.php b/rentabilitaet_detail.php index 55d9bfd..8436997 100755 --- a/rentabilitaet_detail.php +++ b/rentabilitaet_detail.php @@ -340,7 +340,7 @@ function getProfitabilityByMonth($db, $year) $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn_det as fd ON fd.fk_facture_fourn = f.rowid"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = fd.fk_product"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " AND p.fk_product_type = 0"; $sql .= " AND (p.tobuy = 1 OR p.tosell = 1)"; @@ -358,7 +358,7 @@ function getProfitabilityByMonth($db, $year) $sql = "SELECT MONTH(f.datef) as month, SUM(fd.total_ht) as total"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facturedet as fd ON fd.fk_facture = f.rowid"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY MONTH(f.datef)"; diff --git a/ust_detail.php b/ust_detail.php index 6f3d9af..9a6500c 100755 --- a/ust_detail.php +++ b/ust_detail.php @@ -304,7 +304,7 @@ function getVatData($db, $year, $mode = 'quarterly') $sql = "SELECT ".$groupBy." as period, SUM(fd.total_tva) as tva_amount"; $sql .= " FROM ".MAIN_DB_PREFIX."facture as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facturedet as fd ON fd.fk_facture = f.rowid"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY ".$groupBy; @@ -320,10 +320,11 @@ function getVatData($db, $year, $mode = 'quarterly') } // VAT paid - $sql = "SELECT ".$groupBy." as period, SUM(fd.total_tva) as tva_amount"; + // Hinweis: In facture_fourn_det heißt die USt-Spalte 'tva', nicht 'total_tva' wie bei facturedet + $sql = "SELECT ".$groupBy." as period, SUM(fd.tva) as tva_amount"; $sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as f"; $sql .= " INNER JOIN ".MAIN_DB_PREFIX."facture_fourn_det as fd ON fd.fk_facture_fourn = f.rowid"; - $sql .= " WHERE f.fk_statut > 0 AND f.entity = ".((int) $conf->entity); + $sql .= " WHERE f.fk_statut IN (1, 2) AND f.entity = ".((int) $conf->entity); $sql .= " AND YEAR(f.datef) = ".((int) $year); $sql .= " GROUP BY ".$groupBy;